https://mat.midlight.eu/api.php?action=feedcontributions&user=Mat&feedformat=atomProjectWiki - Benutzerbeiträge [de]2024-03-28T23:32:34ZBenutzerbeiträgeMediaWiki 1.31.0https://mat.midlight.eu/index.php?title=Bascom_Precompiler&diff=350Bascom Precompiler2023-11-05T16:51:36Z<p>Mat: /* Index */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [OOP 3/3] Bascom-Precompiler<br />
<br />
<br />
* [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread]<br />
<br />
== Overview ==<br />
The precompiler processes the Bascom source files (also taking includes into account), outputs the generated source files besides the original files and invokes ''bascomp.exe''. Original files are not touched.<br />
It supports the following enhancements:<br />
* Macros with arguments<br />
* RAM and ROM address data type<br />
* Enumerations<br />
* Conditional Compilation Checks: #if Subexist(""), #if Labelexist("")<br />
* Composed data types (structures) and OOP concepts<br />
* Automatic generated constants: _sourceline, _sourcefileline, _filename, _subroutinename, _precompiler, _currentdate, _currenttime, _buildnumber, _freeramstart, _isrstackcount<br />
* Export disassembly (optionally annotated with original source code)<br />
<br />
Some precompiler steps involve parsing the compiler output, in that case, a second compile step is then performed using the gathered data.<br />
<br />
<br />
== Functional description ==<br />
=== Macros with arguments ===<br />
'''Syntax:'''<br />
<pre>Macro [MacroName] ([ArgumentList])<br />
[Statements]<br />
End Macro</pre><br />
These macros work the same as the built-in ones, but accept additional arguments, which work as a textual replacement. Therefore arguments are type-agnostic.<br />
Also supports argument concatenation using "." (argument concatenation has priority over the type-member-/enum-access qualifier "."). Nested macros are allowed.<br />
<br />
<br />
'''Example 1:'''<br />
<pre>Macro Mymacro(printtext)<br />
Print "This is my text: "; printtext<br />
End Macro</pre><br />
<br />
Invoking with a string argument:<br />
<pre>Mymacro "some characters"</pre><br />
Will yield in output:<br />
<pre>Print "This is my text: "; "some characters"</pre><br />
<br />
Invoking with a variable as argument:<br />
<pre>Dim Myvariable As Byte<br />
Mymacro Myvariable</pre><br />
Output:<br />
<pre>Print "This is my text: "; Myvariable</pre><br />
<br />
<br />
'''Example2:'''<br />
<pre>Macro Concat (Arg1, Arg2, Arg3)<br />
Arg1.Arg2 Arg3<br />
End Macro</pre><br />
Invoking:<br />
<pre>Concat($reg, file, "m32def.dat")</pre><br />
Output:<br />
<pre>$regfile "m32def.dat"</pre><br />
<br />
<br />
=== RAM/ROM address data type ===<br />
Some devices have more than 64K program space and use 24 bit wide addresses (instead of 16 bit) for flash access, which makes porting code between the addressing schemes more tedious.<br />
The same happens with RAM addresses for devices using external memory.<br />
To support this, the precompiler introduces two new variable types: ''Rampointer'' and ''Rompointer''.<br />
They will be assigned a ''Word'' or a ''Dword'' variable type on compilation, according to the currently used addressing schemes.<br />
<br />
'''Syntax:'''<br />
<pre>Dim Labeladdress As Rompointer<br />
Dim Myramspace As Rampointer<br />
<br />
Sub Testthis(position As Rampointer)<br />
Local Flashdata As Rompointer<br />
End Sub</pre><br />
<br />
<br />
=== Enumerations ===<br />
Defines a list of key-value pairs. If no value is specified, the value is either 0 for the first key entry or the value of the previous key + 1. One key/value pair per line.<br />
Usage is like constants, members are accessed using ''[EnumName].[MemberName]''.<br />
<br />
'''Syntax:'''<br />
<pre>Enum Myenum<br />
Value1,<br />
Value2 = 5,<br />
Value3<br />
End Enum<br />
<br />
Print Str(Myenum.Value1); Str(Myenum.Value2); Str(Myenum.Value3) ' Output: 056</pre><br />
<br />
=== Conditional Compilation Checks ===<br />
The precompiler adds additional symbol-checks to Bascom's conditional compilation switches (they act in the same way the as built-in ''Varexist("")'' directive).<br />
<br />
'''Syntax:'''<br />
<pre>#if Subexist("[SubroutineName]")</pre><br />
<pre>#if Labelexist("[LabelName]")</pre><br />
<br />
<br />
=== Composed Data Types ===<br />
A composed data type consists of several data types (intrinsic or other composed types) grouped together in memory. <br />
It supports every available data type (bit, byte, word, integer, dword, long, single, double, string, rampointer, rompointer and composed types) and optionally supports inheritance. Composed types as member field or dimensioned using ''Dim'', ''Local'' or as a subroutine parameter are internally referenced by the Rampointer data type.<br />
<br />
To create an instance of the type definition, a memory block of sufficient size has to be reserved and referenced through a variable pointing to the first memory address of that block.<br />
Memberfields are accessed by the "." qualifier. Only assignments of a variable from the memberfield or assignments of the memberfield from a variable or constant are allowed (by now). Internally, these are translated to ''Settype''/''Gettype'' functions as found in ''os_memory.inc''. Bit member fields internally use a byte.<br />
<br />
'''Syntax:'''<br />
<pre>Typedef [TypedefName]<br />
[Extends TypedefName]<br />
[MemberFieldName] As [Bit/Byte/Word/Integer/Dword/Long/Single/Double/String*N/TypedefName]<br />
End Typedef<br />
<br />
Sub Memberfunction(object As [TypedefName])<br />
Local Anotherobject As [TypedefName]<br />
End Sub<br />
<br />
Dim Myinstance As [TypedefName]<br />
<br />
Myinstance = Malloc(SizeOf([TypedefName]))<br />
<br />
Myinstance.[MemberfieldName] = [Constant/Variable]<br />
Variable = Myinstance.[MemberfieldName]<br />
</pre><br />
<br />
<br />
=== Automatic generated constants ===<br />
These keywords are replaced by their value as number or string literal, usage is like constants.<br />
<br />
<br />
<pre>_sourceline</pre><br />
(Number) Returns the current line number, inclusive includes.<br />
<br />
<br />
<pre>_sourcefileline</pre><br />
(Number) Returns the current line number only counting the current file<br />
<br />
<br />
<pre>_filename</pre><br />
(String) Returns the current source file name<br />
<br />
<br />
<pre>_subroutinename</pre><br />
(String) Returns the current sub routine name (valid inside Subs/Functions and Label-Return-pairs, empty string if not inside subroutine).<br />
<br />
<br />
<pre>_precompiler</pre><br />
(Number) Actually creates a constant containing the precompiler version (current: 1), which could be checked for existence by ''#if Varexist("_precompiler")'' to check if the code is beeing processed using the precompiler.<br />
<br />
<br />
<pre>_currenttime</pre><br />
(String) Contains the build time.<br />
<br />
<br />
<pre>_currentdate</pre><br />
(String) Contains the build date.<br />
<br />
<br />
<pre>_freeramstart</pre><br />
(Number) Returns the first unused memory position. Needs a second compile pass.<br />
<br />
<br />
<pre>_isrstackcount</pre><br />
(Number) Returns the used stack size (PUSH statements) inside an ISR. Needs a second compile pass.<br />
<br />
<br />
<pre>Const _buildnumber = 0</pre><br />
This constant is treated specially, in the output it will contain the current build number, incremented by 1 on each compilation. Current build count is maintained in a file besides the original source file with the extension ".bld".<br />
<br />
<br />
=== Precompiler directives ===<br />
<pre>$disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>$disasmmapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
== Command Line ==<br />
'''Syntax:'''<br />
<pre>prebascomp.exe [FileName] [Options]</pre><br />
<br />
<br />
'''Options:'''<br />
<pre>/disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>/mapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
<pre>/log</pre><br />
Generate a log file of the precompilation process.<br />
<br />
<br />
<pre>/registerext</pre><br />
Creates entries in the Windows Explorer context menu for .bas files to directly compile/disassemble using the precompiler.<br />
<br />
<br />
<pre>/unregisterext</pre><br />
Removes the entries from the Windows Explorer context menu.<br />
<br />
<br />
<pre>/update</pre><br />
Force checking of update availability.<br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Object_Oriented_Programming_in_Bascom&diff=349Object Oriented Programming in Bascom2023-11-05T16:51:26Z<p>Mat: /* Index */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [OOP 2/3] Object Oriented Programming in Bascom<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
* [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread]<br />
<br />
== Object as collection of data ==<br />
=== Defining an Object ===<br />
Each object consists of its own member variables (and functions, see below), which are organized in memory as a unit.<br />
For example, here is a simple object (in pseudocode, intended string size in parenthesis), storing some personal information:<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
End Object</pre><br />
<br />
<br />
The member fields therefore have a fixed memory layout, the member's positions could be defined like this:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes</pre><br />
<br />
<br />
To create an instance of an object, a block of memory has to be reserved for the object's data (using either static or dynamic memory allocation).<br />
Further references to the object are done with the starting address of the memory block, the offsets obviously remain the same for each object of the same kind.<br />
The memory map defined above describes the address offsets of the member fields.<br />
Access and manipulation of the field data could then be done by adding the object's memory address and the member field's offset (pseudocode):<br />
<pre>Object.Field = Data ' store data at address + offset<br />
Data = Object.Field ' read data from address + offset</pre><br />
<br />
=== Working with Objects ===<br />
Since Bascom does not support this kind of data access, own routines have to be created (read/write data for each variable type, for example the byte type):<br />
<pre>Sub Setbyte(object As Word, Byval Offset As Word, Byval Value As Byte)<br />
' calculate address + offset<br />
' copy contents of Value to memory address<br />
End Sub<br />
<br />
Function Getbyte(object As Word, Byval Offset As Word) As Byte<br />
' calculate address + offset<br />
' copy contents from memory address to return value<br />
End Function</pre><br />
<br />
<br />
"Interfacing" to the Bascom world needs the help of normal variables:<br />
<pre>Dim Object As Word ' reference to object instance; memory address<br />
<br />
Dim Name As String * 30 ' needs to be big enough to store the string member data<br />
Dim Birth_year As Word<br />
Dim Height_cm As Byte<br />
<br />
' Reserve memory <br />
Object = Malloc(Person_object_size)<br />
<br />
' Fill the object with some data<br />
Name = "John Doe"<br />
Birth_year = 2000<br />
Height_cm = 175<br />
Setstring Object, Person_name, Name<br />
Setword Object, Person_birth_year, Birth_year<br />
Setbyte Object, Person_height_cm, Height_cm<br />
<br />
' do some other things<br />
' ...<br />
<br />
' Get back the object's data<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."</pre><br />
<br />
Implementations of these functions for every data type can be found in the file ''os_memory.inc''.<br />
<br />
<br />
== Member Functions ==<br />
<br />
Lets expand the object definition by a function that prints the informations stored in the object (pseudocode):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object</pre><br />
<br />
<br />
Translated to Bascom, the sub needs to know which object it manipulates, by passing it the object reference:<br />
<pre>Sub Print_info(byref Object As Word)<br />
Local Name As String * 30<br />
Local Birth_year As Word<br />
Local Height_cm As Byte<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."<br />
End Sub</pre><br />
<br />
<br />
== Inheritance and Polymorphism ==<br />
<br />
The pseudocode-application is enhanced to manage two kinds of personal data connected to a person: one type consisting of address fields (street, city, state, ...), the other of email addresses.<br />
Since both of them still should have the same data as the person object from before, the field definitions could be copied to the second type.<br />
Instead, the person object could be extended by either the additional street address data or email address data (the abstracted objects inherit the base object):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object<br />
<br />
Object Street_address<br />
Extends Person<br />
Street As String(50)<br />
Street_nr As String(5)<br />
City As String(20)<br />
State As String(30)<br />
End Object<br />
<br />
Object Email_address<br />
Extends Person<br />
Email As String(50)<br />
End Object</pre><br />
<br />
<br />
The abstracted object definitions ''Street_address'' and ''Email_address'' each contain the fields of the base object ''Person'' (Name, Birth_year and Height_cm) as well as their own specialized fields.<br />
In other words, the abstracted object definitions contain an instance of the base object as additional member field.<br />
<br />
Accordingly, the definitions in Bascom are extended to:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes<br />
<br />
Const Street_address_person = 0 ' the first field consists of the inherited object<br />
Const Street_address_street = Person_object_size ' next field offset is the size of the previous field, object "Person", Street is String * 50<br />
Const Street_address_street_nr = Person_object_size + 51<br />
Const Street_address_city = Person_object_size + 57<br />
Const Street_address_state = Person_object_size + 78<br />
Const Street_address_object_size = Person_object_size + 109<br />
<br />
Const Email_address_person = 0<br />
Const Email_address_email = Person_object_size<br />
Const Email_address_object_size = Person_object_size + 51</pre><br />
<br />
<br />
Note that the inherited object is always the first field of the abstracted object, which leads to the field offsets of the base object always beeing the same along the abstracted objects. The ''Name'' field always starts at position 0, ''Birth_year'' at position 31 and so on.<br />
Because of that, the base member function ''Print_info()'' could as well be used with objects of the type ''Street_address'' or ''Email_address'':<br />
<pre>Dim Personobject As Word<br />
Dim Streetobject As Word<br />
Dim Emailobject As Word<br />
<br />
Dim Tempstring As String * 50<br />
Dim Tempword As Word<br />
<br />
Personobject = Malloc(Person_object_size)<br />
Tempstring = "John Doe"<br />
Setstring Personobject, Person_name, Tempstring<br />
Tempword = 2000<br />
Setword Personobject, Person_birth_year, Tempword<br />
<br />
Streetobject = Malloc(Street_address_object_size)<br />
Tempstring = "Joanne Doe"<br />
Setstring Streetobject, Person_name, Tempstring<br />
Tempword = 2001<br />
Setword Streetobject, Person_birth_year, Tempword<br />
Tempstring = "Doetown"<br />
Setword Streetobject, Street_address_city, Tempstring<br />
<br />
<br />
Emailobject = Malloc(Email_address_object_size)<br />
Tempstring = "Joe Doe"<br />
Setstring Emailobject, Person_name, Tempstring<br />
Tempword = 1970<br />
Setword Emailobject, Person_birth_year, Tempword<br />
Tempstring = "joe@thedoes.com"<br />
Setword Emailobject, Email_address_email, Tempstring<br />
<br />
' ...<br />
<br />
Print_info Personobject<br />
Print_info Streetobject<br />
Print_info Emailobject</pre><br />
<br />
== Examples ==<br />
=== TLSF Memory Allocator ===<br />
Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header.<br />
<br />
<br />
=== Single linked list ===<br />
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again.<br />
First, here is an implementation in an OOP language, VB.net:<br />
<pre>Module Module1<br />
' Object definition<br />
Class ListEntry<br />
Public NextPtr As ListEntry ' point to the next object in the list<br />
Public Size As UInt16 ' store the size of associated data<br />
Public Data As String ' store the data<br />
<br />
' print out information of an object<br />
Public Sub PrintInfo()<br />
Console.Write("Size: " & Size)<br />
Console.WriteLine(vbTab & "Text: " & Data)<br />
End Sub<br />
End Class<br />
<br />
' reference to the first object in the list<br />
Public ListHead As ListEntry = Nothing<br />
<br />
' adds an object to the single linked list<br />
Public Sub ListAdd(Data As String)<br />
Dim Entry As New ListEntry() ' > Malloc<br />
<br />
Entry.NextPtr = ListHead ' link to previous object in list<br />
ListHead = Entry ' set new list head<br />
<br />
Entry.Size = Data.Length ' store data size<br />
Entry.Data = Data ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Public Sub ListRemoveEverySecond()<br />
Dim Entry As ListEntry<br />
Dim NextEntry As ListEntry<br />
Dim DeleteEntry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
DeleteEntry = Entry.NextPtr ' get next entry which is to be deleted<br />
If DeleteEntry IsNot Nothing Then ' object exists<br />
NextEntry = DeleteEntry.NextPtr ' get next object in list<br />
Entry.NextPtr = NextEntry ' update link from previous object<br />
' garbage collection is responsible for freeing the object if there are no references to it anymore<br />
' > Free DeleteObject<br />
Else<br />
NextEntry = Nothing ' no more objects in list<br />
End If<br />
Entry = NextEntry ' continue iterating<br />
End While<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Public Sub ListPrint()<br />
Dim Entry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
Entry.PrintInfo() ' do something with the object<br />
Entry = Entry.NextPtr ' continue with next list entry<br />
End While<br />
End Sub<br />
<br />
Sub Main()<br />
Dim Text As String<br />
Do<br />
Do<br />
Console.Write("Enter text (or ""exit""): ")<br />
Text = Console.ReadLine()<br />
If Text = "exit" Then Exit Do<br />
ListAdd(Text)<br />
Loop<br />
Console.WriteLine("----------------------------------")<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
ListRemoveEverySecond()<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
Loop<br />
End Sub<br />
End Module</pre><br />
<br />
<br />
The same application implemented in Bascom (runs in the simulator):<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition, this represents the memory layout of the object<br />
Const List_next_ptr = 0 ' word (ram address), point to the next object in the list<br />
Const List_data_size_ptr = 2 ' byte, store the size of associated data<br />
Const List_data_ptr = 3 ' string, store the data<br />
Const List_header_size = 3 ' object header size (excl. data)<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Word)<br />
Local Size As Byte<br />
Size = Getbyte(object , List_data_size_ptr)<br />
Print "Size: " ; Size;<br />
Text = Getstring(object , List_data_ptr)<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Object As Word<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Object = Size + List_header_size ' total memory size<br />
Object = Malloc(object) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Setword Object , List_next_ptr , List_head ' link to previous object in list<br />
List_head = Object ' set new list head<br />
<br />
Setbyte Object , List_data_size_ptr , Size ' store data size<br />
Setstring Object , List_data_ptr , Text ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Word<br />
Local Next_object As Word<br />
Local Delete_object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Getword(object , List_next_ptr) ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Getword(delete_object , List_next_ptr) ' get next object in list<br />
Setword Object , List_next_ptr , Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Getword(object , List_next_ptr) ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Word<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
In Bascom using the precompiler:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "..\..\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition<br />
Typedef Stringlist_item<br />
Next_item As Stringlist_item<br />
Data_size As Byte<br />
Stringdata As String * 1<br />
End Typedef<br />
<br />
<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Stringlist_item)<br />
Local Size As Byte<br />
Size = Object.data_size<br />
Print "Size: " ; Size;<br />
Text = Object.stringdata<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Objectsize As Word<br />
Local Object As Stringlist_item<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Objectsize = Typdefsize(stringlist_item) + Size ' total memory size<br />
Object = Malloc(objectsize) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Object.next_item = List_head<br />
List_head = Object ' set new list head<br />
<br />
Object.data_size = Size<br />
Object.stringdata = Text<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Stringlist_item<br />
Local Next_object As Stringlist_item<br />
Local Delete_object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Object.next_item ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Delete_object.next_item ' get next object in list<br />
Object.next_item = Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Object.next_item ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Stringlist_item<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
=== Real world example: Semaphore implementation from Chronos ===<br />
<br />
<pre>'(*****h* /Semaphore ***********************************************************<br />
* DESCRIPTION<br />
* A Semaphore is an object dedicated to task communication.<br />
* It has a defined count of tokens, a task can aquire one as long as there<br />
* is one left. If there are no more tokens left, the queue mode action<br />
* takes place. Every task can release a token.<br />
* SEE ALSO<br />
* /Messagequeue, /Mutex, /Pipe, /Signal, /Syncpipe<br />
')<br />
'******** **********************************************************************<br />
<br />
<br />
$nocompile<br />
<br />
<br />
'(*****O* Semaphore/Os_Semaphore_header ****************************************<br />
* DESCRIPTION<br />
* Header structure of the semaphore object<br />
* DECLARATION<br />
')<br />
Const Os_semaphore_hdr_taskqueue = 0 ' task waiting queue<br />
Const Os_semaphore_hdr_tokencount = Os_taskqueue_hdr_size + 0 ' available tokens<br />
Const Os_semaphore_hdr_tokensize = Os_taskqueue_hdr_size + 1 ' configured token count<br />
Const Os_semaphore_hdr_ownertask = Os_taskqueue_hdr_size + 2 ' task that aquired the last token owns the semaphore<br />
Const Os_semaphore_hdr_size = Os_taskqueue_hdr_size + 4<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
Sub Os_semaphore_create_at(memptr As Word , Byval Tokens As Byte) As Word<br />
Os_mem_clear Memptr , Os_semaphore_hdr_size<br />
<br />
Setbyte Memptr , Os_semaphore_hdr_tokensize , Tokens<br />
Setbyte Memptr , Os_semaphore_hdr_tokencount , Tokens<br />
End Sub<br />
<br />
'(*****f* Semaphore/Os_semaphore_create ****************************************<br />
* DESCRIPTION<br />
* Creates a new semaphore object.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_kill<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_create(byval Tokens As Byte) As Word<br />
'(<br />
* SOURCE<br />
')<br />
Local Semaphore As Word<br />
<br />
Semaphore = Malloc(os_semaphore_hdr_size)<br />
If Semaphore = 0 Then<br />
Os_semaphore_create = 0<br />
Exit Function<br />
End If<br />
Os_semaphore_create_at Semaphore , Tokens<br />
<br />
Os_semaphore_create = Semaphore<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_kill ******************************************<br />
* DESCRIPTION<br />
* Kills a semaphore object<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_create<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_kill(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
If Semaphore <> 0 Then<br />
Os_semaphore_flush Semaphore<br />
Free Semaphore<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_aquire ****************************************<br />
* DESCRIPTION<br />
* Tries to aquire a semaphore token. If there are no tokens left, the queue<br />
* mode action takes place.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_release, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_aquire(byref Semaphore As Word , Byval Queuemode As Word) As Byte<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
'cli<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
' no tokens left, put in waiting list and suspend<br />
' Os_sched_priority_inheritance(os_task_active)<br />
Select Case Queuemode<br />
Case Os_queuemode_noblock:<br />
' return error<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
Case Os_queuemode_block:<br />
' suspend and wait to send a message<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_nowakeup , 0<br />
Case Else<br />
' suspend and wait to send a message or timeout<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_timersingleshot , Queuemode<br />
End Select<br />
Os_sched_taskqueue_insert Semaphore , Os_task_active<br />
'Os_exit_critical<br />
Os_task_suspend Os_task_active<br />
<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
End If<br />
End If<br />
<br />
Decr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
Os_exit_critical<br />
Os_semaphore_aquire = True<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_release ***************************************<br />
* DESCRIPTION<br />
* Releases a semaphore token. The releasing task must not have aquired it<br />
* before.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_release(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
<br />
If Tokencount < Tokensize Then<br />
' release a token<br />
Incr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
' let any waiting task aquire the released token<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task <> 0 Then<br />
Os_task_event Task<br />
End If<br />
Else<br />
Os_exit_critical<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_flush *****************************************<br />
* DESCRIPTION<br />
* Releases all semaphore tokens. The releasing task must not have aquired it<br />
* before. All tasks from the waiting list are put to ready state.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_release<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_flush(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
<br />
' reset tokens<br />
Os_enter_critical<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokensize<br />
Os_exit_critical<br />
<br />
' resume all tasks waiting in the list<br />
Do<br />
Os_enter_critical<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task = 0 Then Exit Do<br />
Os_task_event Task<br />
Loop<br />
End Sub<br />
'******** **********************************************************************</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=348TLSF Dynamic Memory Allocation2023-11-05T16:51:18Z<p>Mat: /* Index */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
* [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread]<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left".<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the remaining free memory size could be found. In this example, there are 7477 bytes left.<br />
<pre><br />
Report : tlsf-xmem-sample<br />
Date : 11-03-2023<br />
Time : 02:51:23<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.8.6<br />
Processor : XM128A1U<br />
SRAM : 2000 hex<br />
EEPROM : 800 hex<br />
ROMSIZE : 22000 hex<br />
<br />
ROMIMAGE : 324A hex -> Will fit into ROM<br />
ROMIMAGE : 12874 dec<br />
FLASH USED : 9 %<br />
UART1 : 0.01 % ERROR<br />
XTAL : 32000000 Hz<br />
<br />
XRAM : 800000 hex<br />
Stack start : 3FFF hex<br />
Stack size : 80 hex<br />
S-Stacksize : 80 hex<br />
S-Stackstart : 3F80 hex<br />
Framesize : 80 hex<br />
Framestart : 3E80 hex<br />
Space left : 7477 dec <------<br />
<br />
LCD DB7 : PORTB.7<br />
LCD DB6 : PORTB.6<br />
LCD DB5 : PORTB.5<br />
LCD DB4 : PORTB.4<br />
LCD E : PORTB.3<br />
LCD RS : PORTB.2<br />
LCD mode : 4 bit<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
DACA0 Word 0318 792<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
=== M32 internal SRAM (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
Const Os_mem_size_free = 1810<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
Free Memoryblock1<br />
Free Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1804<br />
adr1: 210 adr2: 726<br />
Free Memory: 1236<br />
Free Memory: 1804</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7476 ' activated debug info needs an additional byte<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
Const Debug_level_tlsf = 0 ' print out additional debug info, possible values: 0 (no info), 1, 2 and 3 (full info)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
Dim Blocksize As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Blocksize = Os_malloc_blocksize(memoryblock1)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1 ; " Size: " ; Blocksize<br />
<br />
Memoryblock2 = Malloc(48)<br />
Blocksize = Os_malloc_blocksize(memoryblock2)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2 ; " Size: " ; Blocksize<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Blocksize = Os_malloc_blocksize(memoryblock3)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3 ; " Size: " ; Blocksize<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Blocksize = Os_malloc_blocksize(memoryblock4)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4 ; " Size: " ; Blocksize<br />
<br />
' free up allocated memory<br />
Free Memoryblock1<br />
Print "Freed block 1, free memory: " ; Available_memory<br />
<br />
Free Memoryblock2<br />
Print "Freed block 2, free memory: " ; Available_memory<br />
<br />
Free Memoryblock3<br />
Print "Freed block 3, free memory: " ; Available_memory<br />
<br />
Free Memoryblock4<br />
Print "Freed block 4, free memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396068<br />
Alloc 123, free memory: 8395932<br />
Address 1: 8531 Size: 128<br />
Alloc 48, free memory: 8395876<br />
Address 2: 8667 Size: 48<br />
Alloc 10000, free memory: 8385868<br />
Address 3: 16392 Size: 10000<br />
Alloc 12345, free memory: 8373512<br />
Address 4: 26400 Size: 12348<br />
Freed block 1, free memory: 8373640<br />
Freed block 2, free memory: 8373704<br />
Freed block 3, free memory: 8383704<br />
Freed block 4, free memory: 8396068</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Static_Memory_Allocation&diff=347Static Memory Allocation2023-11-05T16:51:05Z<p>Mat: /* Index */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [OOP 1b/3] Static Memory Allocation<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
* [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread]<br />
<br />
== Overview ==<br />
In most applications, dynamic memory allocation is not needed, instead, it is allocated one time (either at the compile-stage or during the boot process) and unchanged during run-time.<br />
Where dynamic memory is not needed, but to maintain compatibility, this static allocation library is used.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable). <br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_static.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left". (refer to [[TLSF_Dynamic_Memory_Allocation#Compiler_report_example|TLSF Dynamic Memory Allocation]]).<br />
<br />
=== Managing memory ===<br />
The library maintains a pointer to the first free memory position, memory requests are served in a consecutive order (incrementing the pointer by the requested size). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library. Releasing memory for use in further requests is not possible.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 1</pre><br />
Define this constant (value doesn't matter) to keep track of the remaining available memory (see below).<br />
<br />
<br />
<pre>Dim Available_memory As Word/Dword</pre><br />
If the constant ''Os_task_dbg_metrics'' is defined, the remaining memory is available to the application via this variable.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Allocate a block of memory. Cannot be freed. Returns 0 if not enough free memory left to serve the request.<br />
<br />
== Samples ==<br />
=== M32 (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 1911<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1911<br />
Free Memory: 1399<br />
adr1: 104 adr2: 616<br />
Free Memory: 1354<br />
</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7742<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1<br />
<br />
Memoryblock2 = Malloc(48)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396350<br />
Alloc 123, free memory: 8396227<br />
Address 1: 8257<br />
Alloc 48, free memory: 8396179<br />
Address 2: 8380<br />
Alloc 10000, free memory: 8378608<br />
Address 3: 16384<br />
Alloc 12345, free memory: 8366263<br />
Address 4: 26384</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=346TLSF Dynamic Memory Allocation2023-11-05T16:50:39Z<p>Mat: /* Index */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
* [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread]<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left".<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the remaining free memory size could be found. In this example, there are 7477 bytes left.<br />
<pre><br />
Report : tlsf-xmem-sample<br />
Date : 11-03-2023<br />
Time : 02:51:23<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.8.6<br />
Processor : XM128A1U<br />
SRAM : 2000 hex<br />
EEPROM : 800 hex<br />
ROMSIZE : 22000 hex<br />
<br />
ROMIMAGE : 324A hex -> Will fit into ROM<br />
ROMIMAGE : 12874 dec<br />
FLASH USED : 9 %<br />
UART1 : 0.01 % ERROR<br />
XTAL : 32000000 Hz<br />
<br />
XRAM : 800000 hex<br />
Stack start : 3FFF hex<br />
Stack size : 80 hex<br />
S-Stacksize : 80 hex<br />
S-Stackstart : 3F80 hex<br />
Framesize : 80 hex<br />
Framestart : 3E80 hex<br />
Space left : 7477 dec <------<br />
<br />
LCD DB7 : PORTB.7<br />
LCD DB6 : PORTB.6<br />
LCD DB5 : PORTB.5<br />
LCD DB4 : PORTB.4<br />
LCD E : PORTB.3<br />
LCD RS : PORTB.2<br />
LCD mode : 4 bit<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
DACA0 Word 0318 792<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
=== M32 internal SRAM (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
Const Os_mem_size_free = 1810<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
Free Memoryblock1<br />
Free Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1804<br />
adr1: 210 adr2: 726<br />
Free Memory: 1236<br />
Free Memory: 1804</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7476 ' activated debug info needs an additional byte<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
Const Debug_level_tlsf = 0 ' print out additional debug info, possible values: 0 (no info), 1, 2 and 3 (full info)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
Dim Blocksize As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Blocksize = Os_malloc_blocksize(memoryblock1)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1 ; " Size: " ; Blocksize<br />
<br />
Memoryblock2 = Malloc(48)<br />
Blocksize = Os_malloc_blocksize(memoryblock2)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2 ; " Size: " ; Blocksize<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Blocksize = Os_malloc_blocksize(memoryblock3)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3 ; " Size: " ; Blocksize<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Blocksize = Os_malloc_blocksize(memoryblock4)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4 ; " Size: " ; Blocksize<br />
<br />
' free up allocated memory<br />
Free Memoryblock1<br />
Print "Freed block 1, free memory: " ; Available_memory<br />
<br />
Free Memoryblock2<br />
Print "Freed block 2, free memory: " ; Available_memory<br />
<br />
Free Memoryblock3<br />
Print "Freed block 3, free memory: " ; Available_memory<br />
<br />
Free Memoryblock4<br />
Print "Freed block 4, free memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396068<br />
Alloc 123, free memory: 8395932<br />
Address 1: 8531 Size: 128<br />
Alloc 48, free memory: 8395876<br />
Address 2: 8667 Size: 48<br />
Alloc 10000, free memory: 8385868<br />
Address 3: 16392 Size: 10000<br />
Alloc 12345, free memory: 8373512<br />
Address 4: 26400 Size: 12348<br />
Freed block 1, free memory: 8373640<br />
Freed block 2, free memory: 8373704<br />
Freed block 3, free memory: 8383704<br />
Freed block 4, free memory: 8396068</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Object_Oriented_Programming_in_Bascom&diff=345Object Oriented Programming in Bascom2023-11-05T16:43:48Z<p>Mat: /* Single linked list */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [OOP 2/3] Object Oriented Programming in Bascom<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Object as collection of data ==<br />
=== Defining an Object ===<br />
Each object consists of its own member variables (and functions, see below), which are organized in memory as a unit.<br />
For example, here is a simple object (in pseudocode, intended string size in parenthesis), storing some personal information:<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
End Object</pre><br />
<br />
<br />
The member fields therefore have a fixed memory layout, the member's positions could be defined like this:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes</pre><br />
<br />
<br />
To create an instance of an object, a block of memory has to be reserved for the object's data (using either static or dynamic memory allocation).<br />
Further references to the object are done with the starting address of the memory block, the offsets obviously remain the same for each object of the same kind.<br />
The memory map defined above describes the address offsets of the member fields.<br />
Access and manipulation of the field data could then be done by adding the object's memory address and the member field's offset (pseudocode):<br />
<pre>Object.Field = Data ' store data at address + offset<br />
Data = Object.Field ' read data from address + offset</pre><br />
<br />
=== Working with Objects ===<br />
Since Bascom does not support this kind of data access, own routines have to be created (read/write data for each variable type, for example the byte type):<br />
<pre>Sub Setbyte(object As Word, Byval Offset As Word, Byval Value As Byte)<br />
' calculate address + offset<br />
' copy contents of Value to memory address<br />
End Sub<br />
<br />
Function Getbyte(object As Word, Byval Offset As Word) As Byte<br />
' calculate address + offset<br />
' copy contents from memory address to return value<br />
End Function</pre><br />
<br />
<br />
"Interfacing" to the Bascom world needs the help of normal variables:<br />
<pre>Dim Object As Word ' reference to object instance; memory address<br />
<br />
Dim Name As String * 30 ' needs to be big enough to store the string member data<br />
Dim Birth_year As Word<br />
Dim Height_cm As Byte<br />
<br />
' Reserve memory <br />
Object = Malloc(Person_object_size)<br />
<br />
' Fill the object with some data<br />
Name = "John Doe"<br />
Birth_year = 2000<br />
Height_cm = 175<br />
Setstring Object, Person_name, Name<br />
Setword Object, Person_birth_year, Birth_year<br />
Setbyte Object, Person_height_cm, Height_cm<br />
<br />
' do some other things<br />
' ...<br />
<br />
' Get back the object's data<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."</pre><br />
<br />
Implementations of these functions for every data type can be found in the file ''os_memory.inc''.<br />
<br />
<br />
== Member Functions ==<br />
<br />
Lets expand the object definition by a function that prints the informations stored in the object (pseudocode):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object</pre><br />
<br />
<br />
Translated to Bascom, the sub needs to know which object it manipulates, by passing it the object reference:<br />
<pre>Sub Print_info(byref Object As Word)<br />
Local Name As String * 30<br />
Local Birth_year As Word<br />
Local Height_cm As Byte<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."<br />
End Sub</pre><br />
<br />
<br />
== Inheritance and Polymorphism ==<br />
<br />
The pseudocode-application is enhanced to manage two kinds of personal data connected to a person: one type consisting of address fields (street, city, state, ...), the other of email addresses.<br />
Since both of them still should have the same data as the person object from before, the field definitions could be copied to the second type.<br />
Instead, the person object could be extended by either the additional street address data or email address data (the abstracted objects inherit the base object):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object<br />
<br />
Object Street_address<br />
Extends Person<br />
Street As String(50)<br />
Street_nr As String(5)<br />
City As String(20)<br />
State As String(30)<br />
End Object<br />
<br />
Object Email_address<br />
Extends Person<br />
Email As String(50)<br />
End Object</pre><br />
<br />
<br />
The abstracted object definitions ''Street_address'' and ''Email_address'' each contain the fields of the base object ''Person'' (Name, Birth_year and Height_cm) as well as their own specialized fields.<br />
In other words, the abstracted object definitions contain an instance of the base object as additional member field.<br />
<br />
Accordingly, the definitions in Bascom are extended to:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes<br />
<br />
Const Street_address_person = 0 ' the first field consists of the inherited object<br />
Const Street_address_street = Person_object_size ' next field offset is the size of the previous field, object "Person", Street is String * 50<br />
Const Street_address_street_nr = Person_object_size + 51<br />
Const Street_address_city = Person_object_size + 57<br />
Const Street_address_state = Person_object_size + 78<br />
Const Street_address_object_size = Person_object_size + 109<br />
<br />
Const Email_address_person = 0<br />
Const Email_address_email = Person_object_size<br />
Const Email_address_object_size = Person_object_size + 51</pre><br />
<br />
<br />
Note that the inherited object is always the first field of the abstracted object, which leads to the field offsets of the base object always beeing the same along the abstracted objects. The ''Name'' field always starts at position 0, ''Birth_year'' at position 31 and so on.<br />
Because of that, the base member function ''Print_info()'' could as well be used with objects of the type ''Street_address'' or ''Email_address'':<br />
<pre>Dim Personobject As Word<br />
Dim Streetobject As Word<br />
Dim Emailobject As Word<br />
<br />
Dim Tempstring As String * 50<br />
Dim Tempword As Word<br />
<br />
Personobject = Malloc(Person_object_size)<br />
Tempstring = "John Doe"<br />
Setstring Personobject, Person_name, Tempstring<br />
Tempword = 2000<br />
Setword Personobject, Person_birth_year, Tempword<br />
<br />
Streetobject = Malloc(Street_address_object_size)<br />
Tempstring = "Joanne Doe"<br />
Setstring Streetobject, Person_name, Tempstring<br />
Tempword = 2001<br />
Setword Streetobject, Person_birth_year, Tempword<br />
Tempstring = "Doetown"<br />
Setword Streetobject, Street_address_city, Tempstring<br />
<br />
<br />
Emailobject = Malloc(Email_address_object_size)<br />
Tempstring = "Joe Doe"<br />
Setstring Emailobject, Person_name, Tempstring<br />
Tempword = 1970<br />
Setword Emailobject, Person_birth_year, Tempword<br />
Tempstring = "joe@thedoes.com"<br />
Setword Emailobject, Email_address_email, Tempstring<br />
<br />
' ...<br />
<br />
Print_info Personobject<br />
Print_info Streetobject<br />
Print_info Emailobject</pre><br />
<br />
== Examples ==<br />
=== TLSF Memory Allocator ===<br />
Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header.<br />
<br />
<br />
=== Single linked list ===<br />
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again.<br />
First, here is an implementation in an OOP language, VB.net:<br />
<pre>Module Module1<br />
' Object definition<br />
Class ListEntry<br />
Public NextPtr As ListEntry ' point to the next object in the list<br />
Public Size As UInt16 ' store the size of associated data<br />
Public Data As String ' store the data<br />
<br />
' print out information of an object<br />
Public Sub PrintInfo()<br />
Console.Write("Size: " & Size)<br />
Console.WriteLine(vbTab & "Text: " & Data)<br />
End Sub<br />
End Class<br />
<br />
' reference to the first object in the list<br />
Public ListHead As ListEntry = Nothing<br />
<br />
' adds an object to the single linked list<br />
Public Sub ListAdd(Data As String)<br />
Dim Entry As New ListEntry() ' > Malloc<br />
<br />
Entry.NextPtr = ListHead ' link to previous object in list<br />
ListHead = Entry ' set new list head<br />
<br />
Entry.Size = Data.Length ' store data size<br />
Entry.Data = Data ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Public Sub ListRemoveEverySecond()<br />
Dim Entry As ListEntry<br />
Dim NextEntry As ListEntry<br />
Dim DeleteEntry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
DeleteEntry = Entry.NextPtr ' get next entry which is to be deleted<br />
If DeleteEntry IsNot Nothing Then ' object exists<br />
NextEntry = DeleteEntry.NextPtr ' get next object in list<br />
Entry.NextPtr = NextEntry ' update link from previous object<br />
' garbage collection is responsible for freeing the object if there are no references to it anymore<br />
' > Free DeleteObject<br />
Else<br />
NextEntry = Nothing ' no more objects in list<br />
End If<br />
Entry = NextEntry ' continue iterating<br />
End While<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Public Sub ListPrint()<br />
Dim Entry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
Entry.PrintInfo() ' do something with the object<br />
Entry = Entry.NextPtr ' continue with next list entry<br />
End While<br />
End Sub<br />
<br />
Sub Main()<br />
Dim Text As String<br />
Do<br />
Do<br />
Console.Write("Enter text (or ""exit""): ")<br />
Text = Console.ReadLine()<br />
If Text = "exit" Then Exit Do<br />
ListAdd(Text)<br />
Loop<br />
Console.WriteLine("----------------------------------")<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
ListRemoveEverySecond()<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
Loop<br />
End Sub<br />
End Module</pre><br />
<br />
<br />
The same application implemented in Bascom (runs in the simulator):<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition, this represents the memory layout of the object<br />
Const List_next_ptr = 0 ' word (ram address), point to the next object in the list<br />
Const List_data_size_ptr = 2 ' byte, store the size of associated data<br />
Const List_data_ptr = 3 ' string, store the data<br />
Const List_header_size = 3 ' object header size (excl. data)<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Word)<br />
Local Size As Byte<br />
Size = Getbyte(object , List_data_size_ptr)<br />
Print "Size: " ; Size;<br />
Text = Getstring(object , List_data_ptr)<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Object As Word<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Object = Size + List_header_size ' total memory size<br />
Object = Malloc(object) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Setword Object , List_next_ptr , List_head ' link to previous object in list<br />
List_head = Object ' set new list head<br />
<br />
Setbyte Object , List_data_size_ptr , Size ' store data size<br />
Setstring Object , List_data_ptr , Text ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Word<br />
Local Next_object As Word<br />
Local Delete_object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Getword(object , List_next_ptr) ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Getword(delete_object , List_next_ptr) ' get next object in list<br />
Setword Object , List_next_ptr , Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Getword(object , List_next_ptr) ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Word<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
In Bascom using the precompiler:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "..\..\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition<br />
Typedef Stringlist_item<br />
Next_item As Stringlist_item<br />
Data_size As Byte<br />
Stringdata As String * 1<br />
End Typedef<br />
<br />
<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Stringlist_item)<br />
Local Size As Byte<br />
Size = Object.data_size<br />
Print "Size: " ; Size;<br />
Text = Object.stringdata<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Objectsize As Word<br />
Local Object As Stringlist_item<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Objectsize = Typdefsize(stringlist_item) + Size ' total memory size<br />
Object = Malloc(objectsize) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Object.next_item = List_head<br />
List_head = Object ' set new list head<br />
<br />
Object.data_size = Size<br />
Object.stringdata = Text<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Stringlist_item<br />
Local Next_object As Stringlist_item<br />
Local Delete_object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Object.next_item ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Delete_object.next_item ' get next object in list<br />
Object.next_item = Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Object.next_item ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Stringlist_item<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
=== Real world example: Semaphore implementation from Chronos ===<br />
<br />
<pre>'(*****h* /Semaphore ***********************************************************<br />
* DESCRIPTION<br />
* A Semaphore is an object dedicated to task communication.<br />
* It has a defined count of tokens, a task can aquire one as long as there<br />
* is one left. If there are no more tokens left, the queue mode action<br />
* takes place. Every task can release a token.<br />
* SEE ALSO<br />
* /Messagequeue, /Mutex, /Pipe, /Signal, /Syncpipe<br />
')<br />
'******** **********************************************************************<br />
<br />
<br />
$nocompile<br />
<br />
<br />
'(*****O* Semaphore/Os_Semaphore_header ****************************************<br />
* DESCRIPTION<br />
* Header structure of the semaphore object<br />
* DECLARATION<br />
')<br />
Const Os_semaphore_hdr_taskqueue = 0 ' task waiting queue<br />
Const Os_semaphore_hdr_tokencount = Os_taskqueue_hdr_size + 0 ' available tokens<br />
Const Os_semaphore_hdr_tokensize = Os_taskqueue_hdr_size + 1 ' configured token count<br />
Const Os_semaphore_hdr_ownertask = Os_taskqueue_hdr_size + 2 ' task that aquired the last token owns the semaphore<br />
Const Os_semaphore_hdr_size = Os_taskqueue_hdr_size + 4<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
Sub Os_semaphore_create_at(memptr As Word , Byval Tokens As Byte) As Word<br />
Os_mem_clear Memptr , Os_semaphore_hdr_size<br />
<br />
Setbyte Memptr , Os_semaphore_hdr_tokensize , Tokens<br />
Setbyte Memptr , Os_semaphore_hdr_tokencount , Tokens<br />
End Sub<br />
<br />
'(*****f* Semaphore/Os_semaphore_create ****************************************<br />
* DESCRIPTION<br />
* Creates a new semaphore object.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_kill<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_create(byval Tokens As Byte) As Word<br />
'(<br />
* SOURCE<br />
')<br />
Local Semaphore As Word<br />
<br />
Semaphore = Malloc(os_semaphore_hdr_size)<br />
If Semaphore = 0 Then<br />
Os_semaphore_create = 0<br />
Exit Function<br />
End If<br />
Os_semaphore_create_at Semaphore , Tokens<br />
<br />
Os_semaphore_create = Semaphore<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_kill ******************************************<br />
* DESCRIPTION<br />
* Kills a semaphore object<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_create<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_kill(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
If Semaphore <> 0 Then<br />
Os_semaphore_flush Semaphore<br />
Free Semaphore<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_aquire ****************************************<br />
* DESCRIPTION<br />
* Tries to aquire a semaphore token. If there are no tokens left, the queue<br />
* mode action takes place.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_release, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_aquire(byref Semaphore As Word , Byval Queuemode As Word) As Byte<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
'cli<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
' no tokens left, put in waiting list and suspend<br />
' Os_sched_priority_inheritance(os_task_active)<br />
Select Case Queuemode<br />
Case Os_queuemode_noblock:<br />
' return error<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
Case Os_queuemode_block:<br />
' suspend and wait to send a message<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_nowakeup , 0<br />
Case Else<br />
' suspend and wait to send a message or timeout<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_timersingleshot , Queuemode<br />
End Select<br />
Os_sched_taskqueue_insert Semaphore , Os_task_active<br />
'Os_exit_critical<br />
Os_task_suspend Os_task_active<br />
<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
End If<br />
End If<br />
<br />
Decr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
Os_exit_critical<br />
Os_semaphore_aquire = True<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_release ***************************************<br />
* DESCRIPTION<br />
* Releases a semaphore token. The releasing task must not have aquired it<br />
* before.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_release(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
<br />
If Tokencount < Tokensize Then<br />
' release a token<br />
Incr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
' let any waiting task aquire the released token<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task <> 0 Then<br />
Os_task_event Task<br />
End If<br />
Else<br />
Os_exit_critical<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_flush *****************************************<br />
* DESCRIPTION<br />
* Releases all semaphore tokens. The releasing task must not have aquired it<br />
* before. All tasks from the waiting list are put to ready state.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_release<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_flush(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
<br />
' reset tokens<br />
Os_enter_critical<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokensize<br />
Os_exit_critical<br />
<br />
' resume all tasks waiting in the list<br />
Do<br />
Os_enter_critical<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task = 0 Then Exit Do<br />
Os_task_event Task<br />
Loop<br />
End Sub<br />
'******** **********************************************************************</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Static_Memory_Allocation&diff=344Static Memory Allocation2023-11-05T16:42:22Z<p>Mat: /* Setup */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [OOP 1b/3] Static Memory Allocation<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
In most applications, dynamic memory allocation is not needed, instead, it is allocated one time (either at the compile-stage or during the boot process) and unchanged during run-time.<br />
Where dynamic memory is not needed, but to maintain compatibility, this static allocation library is used.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable). <br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_static.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left". (refer to [[TLSF_Dynamic_Memory_Allocation#Compiler_report_example|TLSF Dynamic Memory Allocation]]).<br />
<br />
=== Managing memory ===<br />
The library maintains a pointer to the first free memory position, memory requests are served in a consecutive order (incrementing the pointer by the requested size). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library. Releasing memory for use in further requests is not possible.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 1</pre><br />
Define this constant (value doesn't matter) to keep track of the remaining available memory (see below).<br />
<br />
<br />
<pre>Dim Available_memory As Word/Dword</pre><br />
If the constant ''Os_task_dbg_metrics'' is defined, the remaining memory is available to the application via this variable.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Allocate a block of memory. Cannot be freed. Returns 0 if not enough free memory left to serve the request.<br />
<br />
== Samples ==<br />
=== M32 (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 1911<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1911<br />
Free Memory: 1399<br />
adr1: 104 adr2: 616<br />
Free Memory: 1354<br />
</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7742<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1<br />
<br />
Memoryblock2 = Malloc(48)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396350<br />
Alloc 123, free memory: 8396227<br />
Address 1: 8257<br />
Alloc 48, free memory: 8396179<br />
Address 2: 8380<br />
Alloc 10000, free memory: 8378608<br />
Address 3: 16384<br />
Alloc 12345, free memory: 8366263<br />
Address 4: 26384</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=343TLSF Dynamic Memory Allocation2023-11-05T16:40:41Z<p>Mat: /* Download */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left".<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the remaining free memory size could be found. In this example, there are 7477 bytes left.<br />
<pre><br />
Report : tlsf-xmem-sample<br />
Date : 11-03-2023<br />
Time : 02:51:23<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.8.6<br />
Processor : XM128A1U<br />
SRAM : 2000 hex<br />
EEPROM : 800 hex<br />
ROMSIZE : 22000 hex<br />
<br />
ROMIMAGE : 324A hex -> Will fit into ROM<br />
ROMIMAGE : 12874 dec<br />
FLASH USED : 9 %<br />
UART1 : 0.01 % ERROR<br />
XTAL : 32000000 Hz<br />
<br />
XRAM : 800000 hex<br />
Stack start : 3FFF hex<br />
Stack size : 80 hex<br />
S-Stacksize : 80 hex<br />
S-Stackstart : 3F80 hex<br />
Framesize : 80 hex<br />
Framestart : 3E80 hex<br />
Space left : 7477 dec <------<br />
<br />
LCD DB7 : PORTB.7<br />
LCD DB6 : PORTB.6<br />
LCD DB5 : PORTB.5<br />
LCD DB4 : PORTB.4<br />
LCD E : PORTB.3<br />
LCD RS : PORTB.2<br />
LCD mode : 4 bit<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
DACA0 Word 0318 792<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
=== M32 internal SRAM (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
Const Os_mem_size_free = 1810<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
Free Memoryblock1<br />
Free Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1804<br />
adr1: 210 adr2: 726<br />
Free Memory: 1236<br />
Free Memory: 1804</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7476 ' activated debug info needs an additional byte<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
Const Debug_level_tlsf = 0 ' print out additional debug info, possible values: 0 (no info), 1, 2 and 3 (full info)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
Dim Blocksize As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Blocksize = Os_malloc_blocksize(memoryblock1)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1 ; " Size: " ; Blocksize<br />
<br />
Memoryblock2 = Malloc(48)<br />
Blocksize = Os_malloc_blocksize(memoryblock2)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2 ; " Size: " ; Blocksize<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Blocksize = Os_malloc_blocksize(memoryblock3)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3 ; " Size: " ; Blocksize<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Blocksize = Os_malloc_blocksize(memoryblock4)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4 ; " Size: " ; Blocksize<br />
<br />
' free up allocated memory<br />
Free Memoryblock1<br />
Print "Freed block 1, free memory: " ; Available_memory<br />
<br />
Free Memoryblock2<br />
Print "Freed block 2, free memory: " ; Available_memory<br />
<br />
Free Memoryblock3<br />
Print "Freed block 3, free memory: " ; Available_memory<br />
<br />
Free Memoryblock4<br />
Print "Freed block 4, free memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396068<br />
Alloc 123, free memory: 8395932<br />
Address 1: 8531 Size: 128<br />
Alloc 48, free memory: 8395876<br />
Address 2: 8667 Size: 48<br />
Alloc 10000, free memory: 8385868<br />
Address 3: 16392 Size: 10000<br />
Alloc 12345, free memory: 8373512<br />
Address 4: 26400 Size: 12348<br />
Freed block 1, free memory: 8373640<br />
Freed block 2, free memory: 8373704<br />
Freed block 3, free memory: 8383704<br />
Freed block 4, free memory: 8396068</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Static_Memory_Allocation&diff=342Static Memory Allocation2023-11-05T16:40:30Z<p>Mat: /* Download */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [OOP 1b/3] Static Memory Allocation<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
In most applications, dynamic memory allocation is not needed, instead, it is allocated one time (either at the compile-stage or during the boot process) and unchanged during run-time.<br />
Where dynamic memory is not needed, but to maintain compatibility, this static allocation library is used.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable). <br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_static.inc'' into the folder.<br />
<br />
Include the library:<br />
<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_start = ### ' mandatory<br />
$include "inc\os_malloc_static.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.1). It can be found by examining the compile report (with setting ''Options->Compiler->Output->Show internal variables'' enabled), the last variable (with the highest adress) plus the byte size is the adress of the first unused byte (refer to [[TLSF_Dynamic_Memory_Allocation#Compiler_report_example|TLSF Dynamic Memory Allocation]]).<br />
<br />
<br />
=== Managing memory ===<br />
The library maintains a pointer to the first free memory position, memory requests are served in a consecutive order (incrementing the pointer by the requested size). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library. Releasing memory for use in further requests is not possible.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 1</pre><br />
Define this constant (value doesn't matter) to keep track of the remaining available memory (see below).<br />
<br />
<br />
<pre>Dim Available_memory As Word/Dword</pre><br />
If the constant ''Os_task_dbg_metrics'' is defined, the remaining memory is available to the application via this variable.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Allocate a block of memory. Cannot be freed. Returns 0 if not enough free memory left to serve the request.<br />
<br />
== Samples ==<br />
=== M32 (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 1911<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1911<br />
Free Memory: 1399<br />
adr1: 104 adr2: 616<br />
Free Memory: 1354<br />
</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7742<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1<br />
<br />
Memoryblock2 = Malloc(48)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396350<br />
Alloc 123, free memory: 8396227<br />
Address 1: 8257<br />
Alloc 48, free memory: 8396179<br />
Address 2: 8380<br />
Alloc 10000, free memory: 8378608<br />
Address 3: 16384<br />
Alloc 12345, free memory: 8366263<br />
Address 4: 26384</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Object_Oriented_Programming_in_Bascom&diff=341Object Oriented Programming in Bascom2023-11-05T16:40:21Z<p>Mat: /* Download */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [OOP 2/3] Object Oriented Programming in Bascom<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Object as collection of data ==<br />
=== Defining an Object ===<br />
Each object consists of its own member variables (and functions, see below), which are organized in memory as a unit.<br />
For example, here is a simple object (in pseudocode, intended string size in parenthesis), storing some personal information:<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
End Object</pre><br />
<br />
<br />
The member fields therefore have a fixed memory layout, the member's positions could be defined like this:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes</pre><br />
<br />
<br />
To create an instance of an object, a block of memory has to be reserved for the object's data (using either static or dynamic memory allocation).<br />
Further references to the object are done with the starting address of the memory block, the offsets obviously remain the same for each object of the same kind.<br />
The memory map defined above describes the address offsets of the member fields.<br />
Access and manipulation of the field data could then be done by adding the object's memory address and the member field's offset (pseudocode):<br />
<pre>Object.Field = Data ' store data at address + offset<br />
Data = Object.Field ' read data from address + offset</pre><br />
<br />
=== Working with Objects ===<br />
Since Bascom does not support this kind of data access, own routines have to be created (read/write data for each variable type, for example the byte type):<br />
<pre>Sub Setbyte(object As Word, Byval Offset As Word, Byval Value As Byte)<br />
' calculate address + offset<br />
' copy contents of Value to memory address<br />
End Sub<br />
<br />
Function Getbyte(object As Word, Byval Offset As Word) As Byte<br />
' calculate address + offset<br />
' copy contents from memory address to return value<br />
End Function</pre><br />
<br />
<br />
"Interfacing" to the Bascom world needs the help of normal variables:<br />
<pre>Dim Object As Word ' reference to object instance; memory address<br />
<br />
Dim Name As String * 30 ' needs to be big enough to store the string member data<br />
Dim Birth_year As Word<br />
Dim Height_cm As Byte<br />
<br />
' Reserve memory <br />
Object = Malloc(Person_object_size)<br />
<br />
' Fill the object with some data<br />
Name = "John Doe"<br />
Birth_year = 2000<br />
Height_cm = 175<br />
Setstring Object, Person_name, Name<br />
Setword Object, Person_birth_year, Birth_year<br />
Setbyte Object, Person_height_cm, Height_cm<br />
<br />
' do some other things<br />
' ...<br />
<br />
' Get back the object's data<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."</pre><br />
<br />
Implementations of these functions for every data type can be found in the file ''os_memory.inc''.<br />
<br />
<br />
== Member Functions ==<br />
<br />
Lets expand the object definition by a function that prints the informations stored in the object (pseudocode):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object</pre><br />
<br />
<br />
Translated to Bascom, the sub needs to know which object it manipulates, by passing it the object reference:<br />
<pre>Sub Print_info(byref Object As Word)<br />
Local Name As String * 30<br />
Local Birth_year As Word<br />
Local Height_cm As Byte<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."<br />
End Sub</pre><br />
<br />
<br />
== Inheritance and Polymorphism ==<br />
<br />
The pseudocode-application is enhanced to manage two kinds of personal data connected to a person: one type consisting of address fields (street, city, state, ...), the other of email addresses.<br />
Since both of them still should have the same data as the person object from before, the field definitions could be copied to the second type.<br />
Instead, the person object could be extended by either the additional street address data or email address data (the abstracted objects inherit the base object):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object<br />
<br />
Object Street_address<br />
Extends Person<br />
Street As String(50)<br />
Street_nr As String(5)<br />
City As String(20)<br />
State As String(30)<br />
End Object<br />
<br />
Object Email_address<br />
Extends Person<br />
Email As String(50)<br />
End Object</pre><br />
<br />
<br />
The abstracted object definitions ''Street_address'' and ''Email_address'' each contain the fields of the base object ''Person'' (Name, Birth_year and Height_cm) as well as their own specialized fields.<br />
In other words, the abstracted object definitions contain an instance of the base object as additional member field.<br />
<br />
Accordingly, the definitions in Bascom are extended to:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes<br />
<br />
Const Street_address_person = 0 ' the first field consists of the inherited object<br />
Const Street_address_street = Person_object_size ' next field offset is the size of the previous field, object "Person", Street is String * 50<br />
Const Street_address_street_nr = Person_object_size + 51<br />
Const Street_address_city = Person_object_size + 57<br />
Const Street_address_state = Person_object_size + 78<br />
Const Street_address_object_size = Person_object_size + 109<br />
<br />
Const Email_address_person = 0<br />
Const Email_address_email = Person_object_size<br />
Const Email_address_object_size = Person_object_size + 51</pre><br />
<br />
<br />
Note that the inherited object is always the first field of the abstracted object, which leads to the field offsets of the base object always beeing the same along the abstracted objects. The ''Name'' field always starts at position 0, ''Birth_year'' at position 31 and so on.<br />
Because of that, the base member function ''Print_info()'' could as well be used with objects of the type ''Street_address'' or ''Email_address'':<br />
<pre>Dim Personobject As Word<br />
Dim Streetobject As Word<br />
Dim Emailobject As Word<br />
<br />
Dim Tempstring As String * 50<br />
Dim Tempword As Word<br />
<br />
Personobject = Malloc(Person_object_size)<br />
Tempstring = "John Doe"<br />
Setstring Personobject, Person_name, Tempstring<br />
Tempword = 2000<br />
Setword Personobject, Person_birth_year, Tempword<br />
<br />
Streetobject = Malloc(Street_address_object_size)<br />
Tempstring = "Joanne Doe"<br />
Setstring Streetobject, Person_name, Tempstring<br />
Tempword = 2001<br />
Setword Streetobject, Person_birth_year, Tempword<br />
Tempstring = "Doetown"<br />
Setword Streetobject, Street_address_city, Tempstring<br />
<br />
<br />
Emailobject = Malloc(Email_address_object_size)<br />
Tempstring = "Joe Doe"<br />
Setstring Emailobject, Person_name, Tempstring<br />
Tempword = 1970<br />
Setword Emailobject, Person_birth_year, Tempword<br />
Tempstring = "joe@thedoes.com"<br />
Setword Emailobject, Email_address_email, Tempstring<br />
<br />
' ...<br />
<br />
Print_info Personobject<br />
Print_info Streetobject<br />
Print_info Emailobject</pre><br />
<br />
== Examples ==<br />
=== TLSF Memory Allocator ===<br />
Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header.<br />
<br />
<br />
=== Single linked list ===<br />
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again.<br />
First, here is an implementation in an OOP language, VB.net:<br />
<pre>Module Module1<br />
' Object definition<br />
Class ListEntry<br />
Public NextPtr As ListEntry ' point to the next object in the list<br />
Public Size As UInt16 ' store the size of associated data<br />
Public Data As String ' store the data<br />
<br />
' print out information of an object<br />
Public Sub PrintInfo()<br />
Console.Write("Size: " & Size)<br />
Console.WriteLine(vbTab & "Text: " & Data)<br />
End Sub<br />
End Class<br />
<br />
' reference to the first object in the list<br />
Public ListHead As ListEntry = Nothing<br />
<br />
' adds an object to the single linked list<br />
Public Sub ListAdd(Data As String)<br />
Dim Entry As New ListEntry() ' > Malloc<br />
<br />
Entry.NextPtr = ListHead ' link to previous object in list<br />
ListHead = Entry ' set new list head<br />
<br />
Entry.Size = Data.Length ' store data size<br />
Entry.Data = Data ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Public Sub ListRemoveEverySecond()<br />
Dim Entry As ListEntry<br />
Dim NextEntry As ListEntry<br />
Dim DeleteEntry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
DeleteEntry = Entry.NextPtr ' get next entry which is to be deleted<br />
If DeleteEntry IsNot Nothing Then ' object exists<br />
NextEntry = DeleteEntry.NextPtr ' get next object in list<br />
Entry.NextPtr = NextEntry ' update link from previous object<br />
' garbage collection is responsible for freeing the object if there are no references to it anymore<br />
' > Free DeleteObject<br />
Else<br />
NextEntry = Nothing ' no more objects in list<br />
End If<br />
Entry = NextEntry ' continue iterating<br />
End While<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Public Sub ListPrint()<br />
Dim Entry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
Entry.PrintInfo() ' do something with the object<br />
Entry = Entry.NextPtr ' continue with next list entry<br />
End While<br />
End Sub<br />
<br />
Sub Main()<br />
Dim Text As String<br />
Do<br />
Do<br />
Console.Write("Enter text (or ""exit""): ")<br />
Text = Console.ReadLine()<br />
If Text = "exit" Then Exit Do<br />
ListAdd(Text)<br />
Loop<br />
Console.WriteLine("----------------------------------")<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
ListRemoveEverySecond()<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
Loop<br />
End Sub<br />
End Module</pre><br />
<br />
<br />
The same application implemented in Bascom:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition, this represents the memory layout of the object<br />
Const List_next_ptr = 0 ' word (ram address), point to the next object in the list<br />
Const List_data_size_ptr = 2 ' byte, store the size of associated data<br />
Const List_data_ptr = 3 ' string, store the data<br />
Const List_header_size = 3 ' object header size (excl. data)<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Word)<br />
Local Size As Byte<br />
Size = Getbyte(object , List_data_size_ptr)<br />
Print "Size: " ; Size;<br />
Text = Getstring(object , List_data_ptr)<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Object As Word<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Object = Size + List_header_size ' total memory size<br />
Object = Malloc(object) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Setword Object , List_next_ptr , List_head ' link to previous object in list<br />
List_head = Object ' set new list head<br />
<br />
Setbyte Object , List_data_size_ptr , Size ' store data size<br />
Setstring Object , List_data_ptr , Text ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Word<br />
Local Next_object As Word<br />
Local Delete_object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Getword(object , List_next_ptr) ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Getword(delete_object , List_next_ptr) ' get next object in list<br />
Setword Object , List_next_ptr , Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Getword(object , List_next_ptr) ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Word<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
In Bascom using the precompiler:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "..\..\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition<br />
Typedef Stringlist_item<br />
Next_item As Stringlist_item<br />
Data_size As Byte<br />
Stringdata As String * 1<br />
End Typedef<br />
<br />
<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Stringlist_item)<br />
Local Size As Byte<br />
Size = Object.data_size<br />
Print "Size: " ; Size;<br />
Text = Object.stringdata<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Objectsize As Word<br />
Local Object As Stringlist_item<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Objectsize = Typdefsize(stringlist_item) + Size ' total memory size<br />
Object = Malloc(objectsize) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Object.next_item = List_head<br />
List_head = Object ' set new list head<br />
<br />
Object.data_size = Size<br />
Object.stringdata = Text<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Stringlist_item<br />
Local Next_object As Stringlist_item<br />
Local Delete_object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Object.next_item ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Delete_object.next_item ' get next object in list<br />
Object.next_item = Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Object.next_item ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Stringlist_item<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
=== Real world example: Semaphore implementation from Chronos ===<br />
<br />
<pre>'(*****h* /Semaphore ***********************************************************<br />
* DESCRIPTION<br />
* A Semaphore is an object dedicated to task communication.<br />
* It has a defined count of tokens, a task can aquire one as long as there<br />
* is one left. If there are no more tokens left, the queue mode action<br />
* takes place. Every task can release a token.<br />
* SEE ALSO<br />
* /Messagequeue, /Mutex, /Pipe, /Signal, /Syncpipe<br />
')<br />
'******** **********************************************************************<br />
<br />
<br />
$nocompile<br />
<br />
<br />
'(*****O* Semaphore/Os_Semaphore_header ****************************************<br />
* DESCRIPTION<br />
* Header structure of the semaphore object<br />
* DECLARATION<br />
')<br />
Const Os_semaphore_hdr_taskqueue = 0 ' task waiting queue<br />
Const Os_semaphore_hdr_tokencount = Os_taskqueue_hdr_size + 0 ' available tokens<br />
Const Os_semaphore_hdr_tokensize = Os_taskqueue_hdr_size + 1 ' configured token count<br />
Const Os_semaphore_hdr_ownertask = Os_taskqueue_hdr_size + 2 ' task that aquired the last token owns the semaphore<br />
Const Os_semaphore_hdr_size = Os_taskqueue_hdr_size + 4<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
Sub Os_semaphore_create_at(memptr As Word , Byval Tokens As Byte) As Word<br />
Os_mem_clear Memptr , Os_semaphore_hdr_size<br />
<br />
Setbyte Memptr , Os_semaphore_hdr_tokensize , Tokens<br />
Setbyte Memptr , Os_semaphore_hdr_tokencount , Tokens<br />
End Sub<br />
<br />
'(*****f* Semaphore/Os_semaphore_create ****************************************<br />
* DESCRIPTION<br />
* Creates a new semaphore object.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_kill<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_create(byval Tokens As Byte) As Word<br />
'(<br />
* SOURCE<br />
')<br />
Local Semaphore As Word<br />
<br />
Semaphore = Malloc(os_semaphore_hdr_size)<br />
If Semaphore = 0 Then<br />
Os_semaphore_create = 0<br />
Exit Function<br />
End If<br />
Os_semaphore_create_at Semaphore , Tokens<br />
<br />
Os_semaphore_create = Semaphore<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_kill ******************************************<br />
* DESCRIPTION<br />
* Kills a semaphore object<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_create<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_kill(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
If Semaphore <> 0 Then<br />
Os_semaphore_flush Semaphore<br />
Free Semaphore<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_aquire ****************************************<br />
* DESCRIPTION<br />
* Tries to aquire a semaphore token. If there are no tokens left, the queue<br />
* mode action takes place.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_release, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_aquire(byref Semaphore As Word , Byval Queuemode As Word) As Byte<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
'cli<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
' no tokens left, put in waiting list and suspend<br />
' Os_sched_priority_inheritance(os_task_active)<br />
Select Case Queuemode<br />
Case Os_queuemode_noblock:<br />
' return error<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
Case Os_queuemode_block:<br />
' suspend and wait to send a message<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_nowakeup , 0<br />
Case Else<br />
' suspend and wait to send a message or timeout<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_timersingleshot , Queuemode<br />
End Select<br />
Os_sched_taskqueue_insert Semaphore , Os_task_active<br />
'Os_exit_critical<br />
Os_task_suspend Os_task_active<br />
<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
End If<br />
End If<br />
<br />
Decr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
Os_exit_critical<br />
Os_semaphore_aquire = True<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_release ***************************************<br />
* DESCRIPTION<br />
* Releases a semaphore token. The releasing task must not have aquired it<br />
* before.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_release(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
<br />
If Tokencount < Tokensize Then<br />
' release a token<br />
Incr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
' let any waiting task aquire the released token<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task <> 0 Then<br />
Os_task_event Task<br />
End If<br />
Else<br />
Os_exit_critical<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_flush *****************************************<br />
* DESCRIPTION<br />
* Releases all semaphore tokens. The releasing task must not have aquired it<br />
* before. All tasks from the waiting list are put to ready state.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_release<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_flush(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
<br />
' reset tokens<br />
Os_enter_critical<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokensize<br />
Os_exit_critical<br />
<br />
' resume all tasks waiting in the list<br />
Do<br />
Os_enter_critical<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task = 0 Then Exit Do<br />
Os_task_event Task<br />
Loop<br />
End Sub<br />
'******** **********************************************************************</pre><br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Bascom_Precompiler&diff=340Bascom Precompiler2023-11-05T16:40:10Z<p>Mat: /* Download */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [OOP 3/3] Bascom-Precompiler<br />
<br />
<br />
== Overview ==<br />
The precompiler processes the Bascom source files (also taking includes into account), outputs the generated source files besides the original files and invokes ''bascomp.exe''. Original files are not touched.<br />
It supports the following enhancements:<br />
* Macros with arguments<br />
* RAM and ROM address data type<br />
* Enumerations<br />
* Conditional Compilation Checks: #if Subexist(""), #if Labelexist("")<br />
* Composed data types (structures) and OOP concepts<br />
* Automatic generated constants: _sourceline, _sourcefileline, _filename, _subroutinename, _precompiler, _currentdate, _currenttime, _buildnumber, _freeramstart, _isrstackcount<br />
* Export disassembly (optionally annotated with original source code)<br />
<br />
Some precompiler steps involve parsing the compiler output, in that case, a second compile step is then performed using the gathered data.<br />
<br />
<br />
== Functional description ==<br />
=== Macros with arguments ===<br />
'''Syntax:'''<br />
<pre>Macro [MacroName] ([ArgumentList])<br />
[Statements]<br />
End Macro</pre><br />
These macros work the same as the built-in ones, but accept additional arguments, which work as a textual replacement. Therefore arguments are type-agnostic.<br />
Also supports argument concatenation using "." (argument concatenation has priority over the type-member-/enum-access qualifier "."). Nested macros are allowed.<br />
<br />
<br />
'''Example 1:'''<br />
<pre>Macro Mymacro(printtext)<br />
Print "This is my text: "; printtext<br />
End Macro</pre><br />
<br />
Invoking with a string argument:<br />
<pre>Mymacro "some characters"</pre><br />
Will yield in output:<br />
<pre>Print "This is my text: "; "some characters"</pre><br />
<br />
Invoking with a variable as argument:<br />
<pre>Dim Myvariable As Byte<br />
Mymacro Myvariable</pre><br />
Output:<br />
<pre>Print "This is my text: "; Myvariable</pre><br />
<br />
<br />
'''Example2:'''<br />
<pre>Macro Concat (Arg1, Arg2, Arg3)<br />
Arg1.Arg2 Arg3<br />
End Macro</pre><br />
Invoking:<br />
<pre>Concat($reg, file, "m32def.dat")</pre><br />
Output:<br />
<pre>$regfile "m32def.dat"</pre><br />
<br />
<br />
=== RAM/ROM address data type ===<br />
Some devices have more than 64K program space and use 24 bit wide addresses (instead of 16 bit) for flash access, which makes porting code between the addressing schemes more tedious.<br />
The same happens with RAM addresses for devices using external memory.<br />
To support this, the precompiler introduces two new variable types: ''Rampointer'' and ''Rompointer''.<br />
They will be assigned a ''Word'' or a ''Dword'' variable type on compilation, according to the currently used addressing schemes.<br />
<br />
'''Syntax:'''<br />
<pre>Dim Labeladdress As Rompointer<br />
Dim Myramspace As Rampointer<br />
<br />
Sub Testthis(position As Rampointer)<br />
Local Flashdata As Rompointer<br />
End Sub</pre><br />
<br />
<br />
=== Enumerations ===<br />
Defines a list of key-value pairs. If no value is specified, the value is either 0 for the first key entry or the value of the previous key + 1. One key/value pair per line.<br />
Usage is like constants, members are accessed using ''[EnumName].[MemberName]''.<br />
<br />
'''Syntax:'''<br />
<pre>Enum Myenum<br />
Value1,<br />
Value2 = 5,<br />
Value3<br />
End Enum<br />
<br />
Print Str(Myenum.Value1); Str(Myenum.Value2); Str(Myenum.Value3) ' Output: 056</pre><br />
<br />
=== Conditional Compilation Checks ===<br />
The precompiler adds additional symbol-checks to Bascom's conditional compilation switches (they act in the same way the as built-in ''Varexist("")'' directive).<br />
<br />
'''Syntax:'''<br />
<pre>#if Subexist("[SubroutineName]")</pre><br />
<pre>#if Labelexist("[LabelName]")</pre><br />
<br />
<br />
=== Composed Data Types ===<br />
A composed data type consists of several data types (intrinsic or other composed types) grouped together in memory. <br />
It supports every available data type (bit, byte, word, integer, dword, long, single, double, string, rampointer, rompointer and composed types) and optionally supports inheritance. Composed types as member field or dimensioned using ''Dim'', ''Local'' or as a subroutine parameter are internally referenced by the Rampointer data type.<br />
<br />
To create an instance of the type definition, a memory block of sufficient size has to be reserved and referenced through a variable pointing to the first memory address of that block.<br />
Memberfields are accessed by the "." qualifier. Only assignments of a variable from the memberfield or assignments of the memberfield from a variable or constant are allowed (by now). Internally, these are translated to ''Settype''/''Gettype'' functions as found in ''os_memory.inc''. Bit member fields internally use a byte.<br />
<br />
'''Syntax:'''<br />
<pre>Typedef [TypedefName]<br />
[Extends TypedefName]<br />
[MemberFieldName] As [Bit/Byte/Word/Integer/Dword/Long/Single/Double/String*N/TypedefName]<br />
End Typedef<br />
<br />
Sub Memberfunction(object As [TypedefName])<br />
Local Anotherobject As [TypedefName]<br />
End Sub<br />
<br />
Dim Myinstance As [TypedefName]<br />
<br />
Myinstance = Malloc(SizeOf([TypedefName]))<br />
<br />
Myinstance.[MemberfieldName] = [Constant/Variable]<br />
Variable = Myinstance.[MemberfieldName]<br />
</pre><br />
<br />
<br />
=== Automatic generated constants ===<br />
These keywords are replaced by their value as number or string literal, usage is like constants.<br />
<br />
<br />
<pre>_sourceline</pre><br />
(Number) Returns the current line number, inclusive includes.<br />
<br />
<br />
<pre>_sourcefileline</pre><br />
(Number) Returns the current line number only counting the current file<br />
<br />
<br />
<pre>_filename</pre><br />
(String) Returns the current source file name<br />
<br />
<br />
<pre>_subroutinename</pre><br />
(String) Returns the current sub routine name (valid inside Subs/Functions and Label-Return-pairs, empty string if not inside subroutine).<br />
<br />
<br />
<pre>_precompiler</pre><br />
(Number) Actually creates a constant containing the precompiler version (current: 1), which could be checked for existence by ''#if Varexist("_precompiler")'' to check if the code is beeing processed using the precompiler.<br />
<br />
<br />
<pre>_currenttime</pre><br />
(String) Contains the build time.<br />
<br />
<br />
<pre>_currentdate</pre><br />
(String) Contains the build date.<br />
<br />
<br />
<pre>_freeramstart</pre><br />
(Number) Returns the first unused memory position. Needs a second compile pass.<br />
<br />
<br />
<pre>_isrstackcount</pre><br />
(Number) Returns the used stack size (PUSH statements) inside an ISR. Needs a second compile pass.<br />
<br />
<br />
<pre>Const _buildnumber = 0</pre><br />
This constant is treated specially, in the output it will contain the current build number, incremented by 1 on each compilation. Current build count is maintained in a file besides the original source file with the extension ".bld".<br />
<br />
<br />
=== Precompiler directives ===<br />
<pre>$disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>$disasmmapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
== Command Line ==<br />
'''Syntax:'''<br />
<pre>prebascomp.exe [FileName] [Options]</pre><br />
<br />
<br />
'''Options:'''<br />
<pre>/disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>/mapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
<pre>/log</pre><br />
Generate a log file of the precompilation process.<br />
<br />
<br />
<pre>/registerext</pre><br />
Creates entries in the Windows Explorer context menu for .bas files to directly compile/disassemble using the precompiler.<br />
<br />
<br />
<pre>/unregisterext</pre><br />
Removes the entries from the Windows Explorer context menu.<br />
<br />
<br />
<pre>/update</pre><br />
Force checking of update availability.<br />
<br />
== Download ==<br />
<br />
* [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0]</div>Mathttps://mat.midlight.eu/index.php?title=Bascom_Precompiler&diff=339Bascom Precompiler2023-11-04T19:32:57Z<p>Mat: /* Enumerations */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [OOP 3/3] Bascom-Precompiler<br />
<br />
<br />
== Overview ==<br />
The precompiler processes the Bascom source files (also taking includes into account), outputs the generated source files besides the original files and invokes ''bascomp.exe''. Original files are not touched.<br />
It supports the following enhancements:<br />
* Macros with arguments<br />
* RAM and ROM address data type<br />
* Enumerations<br />
* Conditional Compilation Checks: #if Subexist(""), #if Labelexist("")<br />
* Composed data types (structures) and OOP concepts<br />
* Automatic generated constants: _sourceline, _sourcefileline, _filename, _subroutinename, _precompiler, _currentdate, _currenttime, _buildnumber, _freeramstart, _isrstackcount<br />
* Export disassembly (optionally annotated with original source code)<br />
<br />
Some precompiler steps involve parsing the compiler output, in that case, a second compile step is then performed using the gathered data.<br />
<br />
<br />
== Functional description ==<br />
=== Macros with arguments ===<br />
'''Syntax:'''<br />
<pre>Macro [MacroName] ([ArgumentList])<br />
[Statements]<br />
End Macro</pre><br />
These macros work the same as the built-in ones, but accept additional arguments, which work as a textual replacement. Therefore arguments are type-agnostic.<br />
Also supports argument concatenation using "." (argument concatenation has priority over the type-member-/enum-access qualifier "."). Nested macros are allowed.<br />
<br />
<br />
'''Example 1:'''<br />
<pre>Macro Mymacro(printtext)<br />
Print "This is my text: "; printtext<br />
End Macro</pre><br />
<br />
Invoking with a string argument:<br />
<pre>Mymacro "some characters"</pre><br />
Will yield in output:<br />
<pre>Print "This is my text: "; "some characters"</pre><br />
<br />
Invoking with a variable as argument:<br />
<pre>Dim Myvariable As Byte<br />
Mymacro Myvariable</pre><br />
Output:<br />
<pre>Print "This is my text: "; Myvariable</pre><br />
<br />
<br />
'''Example2:'''<br />
<pre>Macro Concat (Arg1, Arg2, Arg3)<br />
Arg1.Arg2 Arg3<br />
End Macro</pre><br />
Invoking:<br />
<pre>Concat($reg, file, "m32def.dat")</pre><br />
Output:<br />
<pre>$regfile "m32def.dat"</pre><br />
<br />
<br />
=== RAM/ROM address data type ===<br />
Some devices have more than 64K program space and use 24 bit wide addresses (instead of 16 bit) for flash access, which makes porting code between the addressing schemes more tedious.<br />
The same happens with RAM addresses for devices using external memory.<br />
To support this, the precompiler introduces two new variable types: ''Rampointer'' and ''Rompointer''.<br />
They will be assigned a ''Word'' or a ''Dword'' variable type on compilation, according to the currently used addressing schemes.<br />
<br />
'''Syntax:'''<br />
<pre>Dim Labeladdress As Rompointer<br />
Dim Myramspace As Rampointer<br />
<br />
Sub Testthis(position As Rampointer)<br />
Local Flashdata As Rompointer<br />
End Sub</pre><br />
<br />
<br />
=== Enumerations ===<br />
Defines a list of key-value pairs. If no value is specified, the value is either 0 for the first key entry or the value of the previous key + 1. One key/value pair per line.<br />
Usage is like constants, members are accessed using ''[EnumName].[MemberName]''.<br />
<br />
'''Syntax:'''<br />
<pre>Enum Myenum<br />
Value1,<br />
Value2 = 5,<br />
Value3<br />
End Enum<br />
<br />
Print Str(Myenum.Value1); Str(Myenum.Value2); Str(Myenum.Value3) ' Output: 056</pre><br />
<br />
=== Conditional Compilation Checks ===<br />
The precompiler adds additional symbol-checks to Bascom's conditional compilation switches (they act in the same way the as built-in ''Varexist("")'' directive).<br />
<br />
'''Syntax:'''<br />
<pre>#if Subexist("[SubroutineName]")</pre><br />
<pre>#if Labelexist("[LabelName]")</pre><br />
<br />
<br />
=== Composed Data Types ===<br />
A composed data type consists of several data types (intrinsic or other composed types) grouped together in memory. <br />
It supports every available data type (bit, byte, word, integer, dword, long, single, double, string, rampointer, rompointer and composed types) and optionally supports inheritance. Composed types as member field or dimensioned using ''Dim'', ''Local'' or as a subroutine parameter are internally referenced by the Rampointer data type.<br />
<br />
To create an instance of the type definition, a memory block of sufficient size has to be reserved and referenced through a variable pointing to the first memory address of that block.<br />
Memberfields are accessed by the "." qualifier. Only assignments of a variable from the memberfield or assignments of the memberfield from a variable or constant are allowed (by now). Internally, these are translated to ''Settype''/''Gettype'' functions as found in ''os_memory.inc''. Bit member fields internally use a byte.<br />
<br />
'''Syntax:'''<br />
<pre>Typedef [TypedefName]<br />
[Extends TypedefName]<br />
[MemberFieldName] As [Bit/Byte/Word/Integer/Dword/Long/Single/Double/String*N/TypedefName]<br />
End Typedef<br />
<br />
Sub Memberfunction(object As [TypedefName])<br />
Local Anotherobject As [TypedefName]<br />
End Sub<br />
<br />
Dim Myinstance As [TypedefName]<br />
<br />
Myinstance = Malloc(SizeOf([TypedefName]))<br />
<br />
Myinstance.[MemberfieldName] = [Constant/Variable]<br />
Variable = Myinstance.[MemberfieldName]<br />
</pre><br />
<br />
<br />
=== Automatic generated constants ===<br />
These keywords are replaced by their value as number or string literal, usage is like constants.<br />
<br />
<br />
<pre>_sourceline</pre><br />
(Number) Returns the current line number, inclusive includes.<br />
<br />
<br />
<pre>_sourcefileline</pre><br />
(Number) Returns the current line number only counting the current file<br />
<br />
<br />
<pre>_filename</pre><br />
(String) Returns the current source file name<br />
<br />
<br />
<pre>_subroutinename</pre><br />
(String) Returns the current sub routine name (valid inside Subs/Functions and Label-Return-pairs, empty string if not inside subroutine).<br />
<br />
<br />
<pre>_precompiler</pre><br />
(Number) Actually creates a constant containing the precompiler version (current: 1), which could be checked for existence by ''#if Varexist("_precompiler")'' to check if the code is beeing processed using the precompiler.<br />
<br />
<br />
<pre>_currenttime</pre><br />
(String) Contains the build time.<br />
<br />
<br />
<pre>_currentdate</pre><br />
(String) Contains the build date.<br />
<br />
<br />
<pre>_freeramstart</pre><br />
(Number) Returns the first unused memory position. Needs a second compile pass.<br />
<br />
<br />
<pre>_isrstackcount</pre><br />
(Number) Returns the used stack size (PUSH statements) inside an ISR. Needs a second compile pass.<br />
<br />
<br />
<pre>Const _buildnumber = 0</pre><br />
This constant is treated specially, in the output it will contain the current build number, incremented by 1 on each compilation. Current build count is maintained in a file besides the original source file with the extension ".bld".<br />
<br />
<br />
=== Precompiler directives ===<br />
<pre>$disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>$disasmmapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
== Command Line ==<br />
'''Syntax:'''<br />
<pre>prebascomp.exe [FileName] [Options]</pre><br />
<br />
<br />
'''Options:'''<br />
<pre>/disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>/mapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
<pre>/log</pre><br />
Generate a log file of the precompilation process.<br />
<br />
<br />
<pre>/registerext</pre><br />
Creates entries in the Windows Explorer context menu for .bas files to directly compile/disassemble using the precompiler.<br />
<br />
<br />
<pre>/unregisterext</pre><br />
Removes the entries from the Windows Explorer context menu.<br />
<br />
<br />
<pre>/update</pre><br />
Force checking of update availability.<br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=Bascom_Precompiler&diff=338Bascom Precompiler2023-11-04T16:36:30Z<p>Mat: /* Command Line */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [OOP 3/3] Bascom-Precompiler<br />
<br />
<br />
== Overview ==<br />
The precompiler processes the Bascom source files (also taking includes into account), outputs the generated source files besides the original files and invokes ''bascomp.exe''. Original files are not touched.<br />
It supports the following enhancements:<br />
* Macros with arguments<br />
* RAM and ROM address data type<br />
* Enumerations<br />
* Conditional Compilation Checks: #if Subexist(""), #if Labelexist("")<br />
* Composed data types (structures) and OOP concepts<br />
* Automatic generated constants: _sourceline, _sourcefileline, _filename, _subroutinename, _precompiler, _currentdate, _currenttime, _buildnumber, _freeramstart, _isrstackcount<br />
* Export disassembly (optionally annotated with original source code)<br />
<br />
Some precompiler steps involve parsing the compiler output, in that case, a second compile step is then performed using the gathered data.<br />
<br />
<br />
== Functional description ==<br />
=== Macros with arguments ===<br />
'''Syntax:'''<br />
<pre>Macro [MacroName] ([ArgumentList])<br />
[Statements]<br />
End Macro</pre><br />
These macros work the same as the built-in ones, but accept additional arguments, which work as a textual replacement. Therefore arguments are type-agnostic.<br />
Also supports argument concatenation using "." (argument concatenation has priority over the type-member-/enum-access qualifier "."). Nested macros are allowed.<br />
<br />
<br />
'''Example 1:'''<br />
<pre>Macro Mymacro(printtext)<br />
Print "This is my text: "; printtext<br />
End Macro</pre><br />
<br />
Invoking with a string argument:<br />
<pre>Mymacro "some characters"</pre><br />
Will yield in output:<br />
<pre>Print "This is my text: "; "some characters"</pre><br />
<br />
Invoking with a variable as argument:<br />
<pre>Dim Myvariable As Byte<br />
Mymacro Myvariable</pre><br />
Output:<br />
<pre>Print "This is my text: "; Myvariable</pre><br />
<br />
<br />
'''Example2:'''<br />
<pre>Macro Concat (Arg1, Arg2, Arg3)<br />
Arg1.Arg2 Arg3<br />
End Macro</pre><br />
Invoking:<br />
<pre>Concat($reg, file, "m32def.dat")</pre><br />
Output:<br />
<pre>$regfile "m32def.dat"</pre><br />
<br />
<br />
=== RAM/ROM address data type ===<br />
Some devices have more than 64K program space and use 24 bit wide addresses (instead of 16 bit) for flash access, which makes porting code between the addressing schemes more tedious.<br />
The same happens with RAM addresses for devices using external memory.<br />
To support this, the precompiler introduces two new variable types: ''Rampointer'' and ''Rompointer''.<br />
They will be assigned a ''Word'' or a ''Dword'' variable type on compilation, according to the currently used addressing schemes.<br />
<br />
'''Syntax:'''<br />
<pre>Dim Labeladdress As Rompointer<br />
Dim Myramspace As Rampointer<br />
<br />
Sub Testthis(position As Rampointer)<br />
Local Flashdata As Rompointer<br />
End Sub</pre><br />
<br />
<br />
=== Enumerations ===<br />
Defines a list of key-value pairs. If no value is specified, the value is either 0 for the first key entry or the value of the previous key + 1. One key/value pair per line.<br />
Usage is like constants, members are accessed using ''[EnumName].[MemberName]''.<br />
<br />
'''Syntax:'''<br />
<pre>Enum Myenum<br />
Value1,<br />
Value2 = 5,<br />
Value3<br />
End Enum<br />
<br />
Print Myenum.Value1; Myenum.Value2; Myenum.Value3 ' Output: 056</pre><br />
<br />
<br />
=== Conditional Compilation Checks ===<br />
The precompiler adds additional symbol-checks to Bascom's conditional compilation switches (they act in the same way the as built-in ''Varexist("")'' directive).<br />
<br />
'''Syntax:'''<br />
<pre>#if Subexist("[SubroutineName]")</pre><br />
<pre>#if Labelexist("[LabelName]")</pre><br />
<br />
<br />
=== Composed Data Types ===<br />
A composed data type consists of several data types (intrinsic or other composed types) grouped together in memory. <br />
It supports every available data type (bit, byte, word, integer, dword, long, single, double, string, rampointer, rompointer and composed types) and optionally supports inheritance. Composed types as member field or dimensioned using ''Dim'', ''Local'' or as a subroutine parameter are internally referenced by the Rampointer data type.<br />
<br />
To create an instance of the type definition, a memory block of sufficient size has to be reserved and referenced through a variable pointing to the first memory address of that block.<br />
Memberfields are accessed by the "." qualifier. Only assignments of a variable from the memberfield or assignments of the memberfield from a variable or constant are allowed (by now). Internally, these are translated to ''Settype''/''Gettype'' functions as found in ''os_memory.inc''. Bit member fields internally use a byte.<br />
<br />
'''Syntax:'''<br />
<pre>Typedef [TypedefName]<br />
[Extends TypedefName]<br />
[MemberFieldName] As [Bit/Byte/Word/Integer/Dword/Long/Single/Double/String*N/TypedefName]<br />
End Typedef<br />
<br />
Sub Memberfunction(object As [TypedefName])<br />
Local Anotherobject As [TypedefName]<br />
End Sub<br />
<br />
Dim Myinstance As [TypedefName]<br />
<br />
Myinstance = Malloc(SizeOf([TypedefName]))<br />
<br />
Myinstance.[MemberfieldName] = [Constant/Variable]<br />
Variable = Myinstance.[MemberfieldName]<br />
</pre><br />
<br />
<br />
=== Automatic generated constants ===<br />
These keywords are replaced by their value as number or string literal, usage is like constants.<br />
<br />
<br />
<pre>_sourceline</pre><br />
(Number) Returns the current line number, inclusive includes.<br />
<br />
<br />
<pre>_sourcefileline</pre><br />
(Number) Returns the current line number only counting the current file<br />
<br />
<br />
<pre>_filename</pre><br />
(String) Returns the current source file name<br />
<br />
<br />
<pre>_subroutinename</pre><br />
(String) Returns the current sub routine name (valid inside Subs/Functions and Label-Return-pairs, empty string if not inside subroutine).<br />
<br />
<br />
<pre>_precompiler</pre><br />
(Number) Actually creates a constant containing the precompiler version (current: 1), which could be checked for existence by ''#if Varexist("_precompiler")'' to check if the code is beeing processed using the precompiler.<br />
<br />
<br />
<pre>_currenttime</pre><br />
(String) Contains the build time.<br />
<br />
<br />
<pre>_currentdate</pre><br />
(String) Contains the build date.<br />
<br />
<br />
<pre>_freeramstart</pre><br />
(Number) Returns the first unused memory position. Needs a second compile pass.<br />
<br />
<br />
<pre>_isrstackcount</pre><br />
(Number) Returns the used stack size (PUSH statements) inside an ISR. Needs a second compile pass.<br />
<br />
<br />
<pre>Const _buildnumber = 0</pre><br />
This constant is treated specially, in the output it will contain the current build number, incremented by 1 on each compilation. Current build count is maintained in a file besides the original source file with the extension ".bld".<br />
<br />
<br />
=== Precompiler directives ===<br />
<pre>$disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>$disasmmapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
== Command Line ==<br />
'''Syntax:'''<br />
<pre>prebascomp.exe [FileName] [Options]</pre><br />
<br />
<br />
'''Options:'''<br />
<pre>/disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>/mapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
<pre>/log</pre><br />
Generate a log file of the precompilation process.<br />
<br />
<br />
<pre>/registerext</pre><br />
Creates entries in the Windows Explorer context menu for .bas files to directly compile/disassemble using the precompiler.<br />
<br />
<br />
<pre>/unregisterext</pre><br />
Removes the entries from the Windows Explorer context menu.<br />
<br />
<br />
<pre>/update</pre><br />
Force checking of update availability.<br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=Bascom_Precompiler&diff=337Bascom Precompiler2023-11-04T16:35:53Z<p>Mat: /* Command Line */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [OOP 3/3] Bascom-Precompiler<br />
<br />
<br />
== Overview ==<br />
The precompiler processes the Bascom source files (also taking includes into account), outputs the generated source files besides the original files and invokes ''bascomp.exe''. Original files are not touched.<br />
It supports the following enhancements:<br />
* Macros with arguments<br />
* RAM and ROM address data type<br />
* Enumerations<br />
* Conditional Compilation Checks: #if Subexist(""), #if Labelexist("")<br />
* Composed data types (structures) and OOP concepts<br />
* Automatic generated constants: _sourceline, _sourcefileline, _filename, _subroutinename, _precompiler, _currentdate, _currenttime, _buildnumber, _freeramstart, _isrstackcount<br />
* Export disassembly (optionally annotated with original source code)<br />
<br />
Some precompiler steps involve parsing the compiler output, in that case, a second compile step is then performed using the gathered data.<br />
<br />
<br />
== Functional description ==<br />
=== Macros with arguments ===<br />
'''Syntax:'''<br />
<pre>Macro [MacroName] ([ArgumentList])<br />
[Statements]<br />
End Macro</pre><br />
These macros work the same as the built-in ones, but accept additional arguments, which work as a textual replacement. Therefore arguments are type-agnostic.<br />
Also supports argument concatenation using "." (argument concatenation has priority over the type-member-/enum-access qualifier "."). Nested macros are allowed.<br />
<br />
<br />
'''Example 1:'''<br />
<pre>Macro Mymacro(printtext)<br />
Print "This is my text: "; printtext<br />
End Macro</pre><br />
<br />
Invoking with a string argument:<br />
<pre>Mymacro "some characters"</pre><br />
Will yield in output:<br />
<pre>Print "This is my text: "; "some characters"</pre><br />
<br />
Invoking with a variable as argument:<br />
<pre>Dim Myvariable As Byte<br />
Mymacro Myvariable</pre><br />
Output:<br />
<pre>Print "This is my text: "; Myvariable</pre><br />
<br />
<br />
'''Example2:'''<br />
<pre>Macro Concat (Arg1, Arg2, Arg3)<br />
Arg1.Arg2 Arg3<br />
End Macro</pre><br />
Invoking:<br />
<pre>Concat($reg, file, "m32def.dat")</pre><br />
Output:<br />
<pre>$regfile "m32def.dat"</pre><br />
<br />
<br />
=== RAM/ROM address data type ===<br />
Some devices have more than 64K program space and use 24 bit wide addresses (instead of 16 bit) for flash access, which makes porting code between the addressing schemes more tedious.<br />
The same happens with RAM addresses for devices using external memory.<br />
To support this, the precompiler introduces two new variable types: ''Rampointer'' and ''Rompointer''.<br />
They will be assigned a ''Word'' or a ''Dword'' variable type on compilation, according to the currently used addressing schemes.<br />
<br />
'''Syntax:'''<br />
<pre>Dim Labeladdress As Rompointer<br />
Dim Myramspace As Rampointer<br />
<br />
Sub Testthis(position As Rampointer)<br />
Local Flashdata As Rompointer<br />
End Sub</pre><br />
<br />
<br />
=== Enumerations ===<br />
Defines a list of key-value pairs. If no value is specified, the value is either 0 for the first key entry or the value of the previous key + 1. One key/value pair per line.<br />
Usage is like constants, members are accessed using ''[EnumName].[MemberName]''.<br />
<br />
'''Syntax:'''<br />
<pre>Enum Myenum<br />
Value1,<br />
Value2 = 5,<br />
Value3<br />
End Enum<br />
<br />
Print Myenum.Value1; Myenum.Value2; Myenum.Value3 ' Output: 056</pre><br />
<br />
<br />
=== Conditional Compilation Checks ===<br />
The precompiler adds additional symbol-checks to Bascom's conditional compilation switches (they act in the same way the as built-in ''Varexist("")'' directive).<br />
<br />
'''Syntax:'''<br />
<pre>#if Subexist("[SubroutineName]")</pre><br />
<pre>#if Labelexist("[LabelName]")</pre><br />
<br />
<br />
=== Composed Data Types ===<br />
A composed data type consists of several data types (intrinsic or other composed types) grouped together in memory. <br />
It supports every available data type (bit, byte, word, integer, dword, long, single, double, string, rampointer, rompointer and composed types) and optionally supports inheritance. Composed types as member field or dimensioned using ''Dim'', ''Local'' or as a subroutine parameter are internally referenced by the Rampointer data type.<br />
<br />
To create an instance of the type definition, a memory block of sufficient size has to be reserved and referenced through a variable pointing to the first memory address of that block.<br />
Memberfields are accessed by the "." qualifier. Only assignments of a variable from the memberfield or assignments of the memberfield from a variable or constant are allowed (by now). Internally, these are translated to ''Settype''/''Gettype'' functions as found in ''os_memory.inc''. Bit member fields internally use a byte.<br />
<br />
'''Syntax:'''<br />
<pre>Typedef [TypedefName]<br />
[Extends TypedefName]<br />
[MemberFieldName] As [Bit/Byte/Word/Integer/Dword/Long/Single/Double/String*N/TypedefName]<br />
End Typedef<br />
<br />
Sub Memberfunction(object As [TypedefName])<br />
Local Anotherobject As [TypedefName]<br />
End Sub<br />
<br />
Dim Myinstance As [TypedefName]<br />
<br />
Myinstance = Malloc(SizeOf([TypedefName]))<br />
<br />
Myinstance.[MemberfieldName] = [Constant/Variable]<br />
Variable = Myinstance.[MemberfieldName]<br />
</pre><br />
<br />
<br />
=== Automatic generated constants ===<br />
These keywords are replaced by their value as number or string literal, usage is like constants.<br />
<br />
<br />
<pre>_sourceline</pre><br />
(Number) Returns the current line number, inclusive includes.<br />
<br />
<br />
<pre>_sourcefileline</pre><br />
(Number) Returns the current line number only counting the current file<br />
<br />
<br />
<pre>_filename</pre><br />
(String) Returns the current source file name<br />
<br />
<br />
<pre>_subroutinename</pre><br />
(String) Returns the current sub routine name (valid inside Subs/Functions and Label-Return-pairs, empty string if not inside subroutine).<br />
<br />
<br />
<pre>_precompiler</pre><br />
(Number) Actually creates a constant containing the precompiler version (current: 1), which could be checked for existence by ''#if Varexist("_precompiler")'' to check if the code is beeing processed using the precompiler.<br />
<br />
<br />
<pre>_currenttime</pre><br />
(String) Contains the build time.<br />
<br />
<br />
<pre>_currentdate</pre><br />
(String) Contains the build date.<br />
<br />
<br />
<pre>_freeramstart</pre><br />
(Number) Returns the first unused memory position. Needs a second compile pass.<br />
<br />
<br />
<pre>_isrstackcount</pre><br />
(Number) Returns the used stack size (PUSH statements) inside an ISR. Needs a second compile pass.<br />
<br />
<br />
<pre>Const _buildnumber = 0</pre><br />
This constant is treated specially, in the output it will contain the current build number, incremented by 1 on each compilation. Current build count is maintained in a file besides the original source file with the extension ".bld".<br />
<br />
<br />
=== Precompiler directives ===<br />
<pre>$disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>$disasmmapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
== Command Line ==<br />
'''Syntax:'''<br />
<pre>prebascomp.exe [FileName] [Options]</pre><br />
<br />
<br />
'''Options:'''<br />
<pre>/disasm</pre><br />
Instructs the precompiler to output a disassembly of the compilation result. Needs a second compile pass.<br />
<br />
<br />
<pre>/mapped</pre><br />
Instructs the precompiler to output a disassembly of the compilation result, mapped to the corresponding source lines. Needs a second compile pass.<br />
<br />
<br />
<pre>/log</pre><br />
Generate a log file of the precompilation process.<br />
<br />
<br />
<pre>/registerext</pre><br />
Creates entries in the Windows Explorer context menu for .bas files to directly compile/disassemble using the precompiler.<br />
<br />
<br />
<pre>/unregisterext</pre><br />
Removes the entries from the Windows Explorer context menu.<br />
<br />
<br />
<pre>/update</pre><br />
Force checking of update availability<br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=Object_Oriented_Programming_in_Bascom&diff=336Object Oriented Programming in Bascom2023-11-04T16:30:19Z<p>Mat: /* Examples */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [OOP 2/3] Object Oriented Programming in Bascom<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Object as collection of data ==<br />
=== Defining an Object ===<br />
Each object consists of its own member variables (and functions, see below), which are organized in memory as a unit.<br />
For example, here is a simple object (in pseudocode, intended string size in parenthesis), storing some personal information:<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
End Object</pre><br />
<br />
<br />
The member fields therefore have a fixed memory layout, the member's positions could be defined like this:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes</pre><br />
<br />
<br />
To create an instance of an object, a block of memory has to be reserved for the object's data (using either static or dynamic memory allocation).<br />
Further references to the object are done with the starting address of the memory block, the offsets obviously remain the same for each object of the same kind.<br />
The memory map defined above describes the address offsets of the member fields.<br />
Access and manipulation of the field data could then be done by adding the object's memory address and the member field's offset (pseudocode):<br />
<pre>Object.Field = Data ' store data at address + offset<br />
Data = Object.Field ' read data from address + offset</pre><br />
<br />
=== Working with Objects ===<br />
Since Bascom does not support this kind of data access, own routines have to be created (read/write data for each variable type, for example the byte type):<br />
<pre>Sub Setbyte(object As Word, Byval Offset As Word, Byval Value As Byte)<br />
' calculate address + offset<br />
' copy contents of Value to memory address<br />
End Sub<br />
<br />
Function Getbyte(object As Word, Byval Offset As Word) As Byte<br />
' calculate address + offset<br />
' copy contents from memory address to return value<br />
End Function</pre><br />
<br />
<br />
"Interfacing" to the Bascom world needs the help of normal variables:<br />
<pre>Dim Object As Word ' reference to object instance; memory address<br />
<br />
Dim Name As String * 30 ' needs to be big enough to store the string member data<br />
Dim Birth_year As Word<br />
Dim Height_cm As Byte<br />
<br />
' Reserve memory <br />
Object = Malloc(Person_object_size)<br />
<br />
' Fill the object with some data<br />
Name = "John Doe"<br />
Birth_year = 2000<br />
Height_cm = 175<br />
Setstring Object, Person_name, Name<br />
Setword Object, Person_birth_year, Birth_year<br />
Setbyte Object, Person_height_cm, Height_cm<br />
<br />
' do some other things<br />
' ...<br />
<br />
' Get back the object's data<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."</pre><br />
<br />
Implementations of these functions for every data type can be found in the file ''os_memory.inc''.<br />
<br />
<br />
== Member Functions ==<br />
<br />
Lets expand the object definition by a function that prints the informations stored in the object (pseudocode):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object</pre><br />
<br />
<br />
Translated to Bascom, the sub needs to know which object it manipulates, by passing it the object reference:<br />
<pre>Sub Print_info(byref Object As Word)<br />
Local Name As String * 30<br />
Local Birth_year As Word<br />
Local Height_cm As Byte<br />
Name = Getstring(Object, Person_name)<br />
Birth_year = Getword(Object, Person_birth_year)<br />
Height_cm = Getbyte(Object, Person_height_cm)<br />
Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."<br />
End Sub</pre><br />
<br />
<br />
== Inheritance and Polymorphism ==<br />
<br />
The pseudocode-application is enhanced to manage two kinds of personal data connected to a person: one type consisting of address fields (street, city, state, ...), the other of email addresses.<br />
Since both of them still should have the same data as the person object from before, the field definitions could be copied to the second type.<br />
Instead, the person object could be extended by either the additional street address data or email address data (the abstracted objects inherit the base object):<br />
<pre>Object Person<br />
Name As String(30)<br />
Birth_year As Word<br />
Height_cm As Byte<br />
<br />
Sub Print_info()<br />
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm<br />
End Sub<br />
End Object<br />
<br />
Object Street_address<br />
Extends Person<br />
Street As String(50)<br />
Street_nr As String(5)<br />
City As String(20)<br />
State As String(30)<br />
End Object<br />
<br />
Object Email_address<br />
Extends Person<br />
Email As String(50)<br />
End Object</pre><br />
<br />
<br />
The abstracted object definitions ''Street_address'' and ''Email_address'' each contain the fields of the base object ''Person'' (Name, Birth_year and Height_cm) as well as their own specialized fields.<br />
In other words, the abstracted object definitions contain an instance of the base object as additional member field.<br />
<br />
Accordingly, the definitions in Bascom are extended to:<br />
<pre>Const Person_name = 0 ' Name string is starting at position 0<br />
Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator<br />
Const Person_height_cm = 33 ' adding 2 bytes of previous field (word)<br />
Const Person_object_size = 34 ' needing a total of 34 bytes<br />
<br />
Const Street_address_person = 0 ' the first field consists of the inherited object<br />
Const Street_address_street = Person_object_size ' next field offset is the size of the previous field, object "Person", Street is String * 50<br />
Const Street_address_street_nr = Person_object_size + 51<br />
Const Street_address_city = Person_object_size + 57<br />
Const Street_address_state = Person_object_size + 78<br />
Const Street_address_object_size = Person_object_size + 109<br />
<br />
Const Email_address_person = 0<br />
Const Email_address_email = Person_object_size<br />
Const Email_address_object_size = Person_object_size + 51</pre><br />
<br />
<br />
Note that the inherited object is always the first field of the abstracted object, which leads to the field offsets of the base object always beeing the same along the abstracted objects. The ''Name'' field always starts at position 0, ''Birth_year'' at position 31 and so on.<br />
Because of that, the base member function ''Print_info()'' could as well be used with objects of the type ''Street_address'' or ''Email_address'':<br />
<pre>Dim Personobject As Word<br />
Dim Streetobject As Word<br />
Dim Emailobject As Word<br />
<br />
Dim Tempstring As String * 50<br />
Dim Tempword As Word<br />
<br />
Personobject = Malloc(Person_object_size)<br />
Tempstring = "John Doe"<br />
Setstring Personobject, Person_name, Tempstring<br />
Tempword = 2000<br />
Setword Personobject, Person_birth_year, Tempword<br />
<br />
Streetobject = Malloc(Street_address_object_size)<br />
Tempstring = "Joanne Doe"<br />
Setstring Streetobject, Person_name, Tempstring<br />
Tempword = 2001<br />
Setword Streetobject, Person_birth_year, Tempword<br />
Tempstring = "Doetown"<br />
Setword Streetobject, Street_address_city, Tempstring<br />
<br />
<br />
Emailobject = Malloc(Email_address_object_size)<br />
Tempstring = "Joe Doe"<br />
Setstring Emailobject, Person_name, Tempstring<br />
Tempword = 1970<br />
Setword Emailobject, Person_birth_year, Tempword<br />
Tempstring = "joe@thedoes.com"<br />
Setword Emailobject, Email_address_email, Tempstring<br />
<br />
' ...<br />
<br />
Print_info Personobject<br />
Print_info Streetobject<br />
Print_info Emailobject</pre><br />
<br />
== Examples ==<br />
=== TLSF Memory Allocator ===<br />
Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header.<br />
<br />
<br />
=== Single linked list ===<br />
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again.<br />
First, here is an implementation in an OOP language, VB.net:<br />
<pre>Module Module1<br />
' Object definition<br />
Class ListEntry<br />
Public NextPtr As ListEntry ' point to the next object in the list<br />
Public Size As UInt16 ' store the size of associated data<br />
Public Data As String ' store the data<br />
<br />
' print out information of an object<br />
Public Sub PrintInfo()<br />
Console.Write("Size: " & Size)<br />
Console.WriteLine(vbTab & "Text: " & Data)<br />
End Sub<br />
End Class<br />
<br />
' reference to the first object in the list<br />
Public ListHead As ListEntry = Nothing<br />
<br />
' adds an object to the single linked list<br />
Public Sub ListAdd(Data As String)<br />
Dim Entry As New ListEntry() ' > Malloc<br />
<br />
Entry.NextPtr = ListHead ' link to previous object in list<br />
ListHead = Entry ' set new list head<br />
<br />
Entry.Size = Data.Length ' store data size<br />
Entry.Data = Data ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Public Sub ListRemoveEverySecond()<br />
Dim Entry As ListEntry<br />
Dim NextEntry As ListEntry<br />
Dim DeleteEntry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
DeleteEntry = Entry.NextPtr ' get next entry which is to be deleted<br />
If DeleteEntry IsNot Nothing Then ' object exists<br />
NextEntry = DeleteEntry.NextPtr ' get next object in list<br />
Entry.NextPtr = NextEntry ' update link from previous object<br />
' garbage collection is responsible for freeing the object if there are no references to it anymore<br />
' > Free DeleteObject<br />
Else<br />
NextEntry = Nothing ' no more objects in list<br />
End If<br />
Entry = NextEntry ' continue iterating<br />
End While<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Public Sub ListPrint()<br />
Dim Entry As ListEntry<br />
<br />
Entry = ListHead ' begin iterating<br />
While Entry IsNot Nothing ' as long as there is an object available<br />
Entry.PrintInfo() ' do something with the object<br />
Entry = Entry.NextPtr ' continue with next list entry<br />
End While<br />
End Sub<br />
<br />
Sub Main()<br />
Dim Text As String<br />
Do<br />
Do<br />
Console.Write("Enter text (or ""exit""): ")<br />
Text = Console.ReadLine()<br />
If Text = "exit" Then Exit Do<br />
ListAdd(Text)<br />
Loop<br />
Console.WriteLine("----------------------------------")<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
ListRemoveEverySecond()<br />
ListPrint()<br />
Console.WriteLine("----------------------------------")<br />
Loop<br />
End Sub<br />
End Module</pre><br />
<br />
<br />
The same application implemented in Bascom:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition, this represents the memory layout of the object<br />
Const List_next_ptr = 0 ' word (ram address), point to the next object in the list<br />
Const List_data_size_ptr = 2 ' byte, store the size of associated data<br />
Const List_data_ptr = 3 ' string, store the data<br />
Const List_header_size = 3 ' object header size (excl. data)<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Word)<br />
Local Size As Byte<br />
Size = Getbyte(object , List_data_size_ptr)<br />
Print "Size: " ; Size;<br />
Text = Getstring(object , List_data_ptr)<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Object As Word<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Object = Size + List_header_size ' total memory size<br />
Object = Malloc(object) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Setword Object , List_next_ptr , List_head ' link to previous object in list<br />
List_head = Object ' set new list head<br />
<br />
Setbyte Object , List_data_size_ptr , Size ' store data size<br />
Setstring Object , List_data_ptr , Text ' store data<br />
End Sub<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Word<br />
Local Next_object As Word<br />
Local Delete_object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Getword(object , List_next_ptr) ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Getword(delete_object , List_next_ptr) ' get next object in list<br />
Setword Object , List_next_ptr , Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Word<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Getword(object , List_next_ptr) ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Word<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
In Bascom using the precompiler:<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 16000000<br />
$hwstack = 32<br />
$swstack = 48<br />
$framesize = 64<br />
$baud = 9600<br />
<br />
Config Submode = New<br />
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0<br />
<br />
<br />
<br />
' uncomment to view detailed debug output<br />
'Const Debug_level_tlsf = 3<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
'Const Os_mem_start_free = 250<br />
<br />
' include needed libraries<br />
$include "..\..\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' Object definition<br />
Typedef Stringlist_item<br />
Next_item As Stringlist_item<br />
Data_size As Byte<br />
Stringdata As String * 1<br />
End Typedef<br />
<br />
<br />
<br />
' print out information of an object<br />
Sub Print_info(byref Object As Stringlist_item)<br />
Local Size As Byte<br />
Size = Object.data_size<br />
Print "Size: " ; Size;<br />
Text = Object.stringdata<br />
Print "{009}Text: " ; Text<br />
End Sub<br />
<br />
<br />
<br />
' adds an object to the single linked list<br />
Sub List_add(byref Text As String)<br />
Local Size As Byte<br />
Local Objectsize As Word<br />
Local Object As Stringlist_item<br />
<br />
Size = Len(text)<br />
Size = Size + 1 ' string trailing zero byte<br />
Objectsize = Typdefsize(stringlist_item) + Size ' total memory size<br />
Object = Malloc(objectsize) ' try to allocate memory<br />
If Object = 0 Then Exit Sub ' check if successful<br />
<br />
Object.next_item = List_head<br />
List_head = Object ' set new list head<br />
<br />
Object.data_size = Size<br />
Object.stringdata = Text<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and deletes every second object<br />
Sub List_remove_every_second()<br />
Local Object As Stringlist_item<br />
Local Next_object As Stringlist_item<br />
Local Delete_object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Delete_object = Object.next_item ' get next entry which is to be deleted<br />
If Delete_object <> 0 Then ' object exists<br />
Next_object = Delete_object.next_item ' get next object in list<br />
Object.next_item = Next_object ' update link from previous object<br />
Free Delete_object ' delete actual object<br />
Else<br />
Next_object = 0 ' no more objects in list<br />
End If<br />
Object = Next_object ' continue iterating<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' iterates through the list and prints out information about the stored objects<br />
Sub List_print()<br />
Local Object As Stringlist_item<br />
<br />
Object = List_head ' begin iterating<br />
While Object <> 0 ' as long as there is an object available<br />
Print_info Object ' do something with the object<br />
Object = Object.next_item ' continue with next list entry<br />
Wend<br />
End Sub<br />
<br />
<br />
<br />
' reference to the first object in the list<br />
Dim List_head As Stringlist_item<br />
<br />
Dim Text As String * 50<br />
<br />
Do<br />
' collect strings from the console and add to list<br />
Do<br />
Input "Enter text (or {034}exit{034}): " , Text<br />
If Text = "exit" Then Exit Do<br />
List_add Text<br />
Loop<br />
<br />
' print out list of strings<br />
Print "----------------------------------"<br />
List_print<br />
<br />
' modify list and print it out again<br />
Print "----------------------------------"<br />
List_remove_every_second<br />
List_print<br />
Print "----------------------------------"<br />
Loop</pre><br />
<br />
<br />
=== Real world example: Semaphore implementation from Chronos ===<br />
<br />
<pre>'(*****h* /Semaphore ***********************************************************<br />
* DESCRIPTION<br />
* A Semaphore is an object dedicated to task communication.<br />
* It has a defined count of tokens, a task can aquire one as long as there<br />
* is one left. If there are no more tokens left, the queue mode action<br />
* takes place. Every task can release a token.<br />
* SEE ALSO<br />
* /Messagequeue, /Mutex, /Pipe, /Signal, /Syncpipe<br />
')<br />
'******** **********************************************************************<br />
<br />
<br />
$nocompile<br />
<br />
<br />
'(*****O* Semaphore/Os_Semaphore_header ****************************************<br />
* DESCRIPTION<br />
* Header structure of the semaphore object<br />
* DECLARATION<br />
')<br />
Const Os_semaphore_hdr_taskqueue = 0 ' task waiting queue<br />
Const Os_semaphore_hdr_tokencount = Os_taskqueue_hdr_size + 0 ' available tokens<br />
Const Os_semaphore_hdr_tokensize = Os_taskqueue_hdr_size + 1 ' configured token count<br />
Const Os_semaphore_hdr_ownertask = Os_taskqueue_hdr_size + 2 ' task that aquired the last token owns the semaphore<br />
Const Os_semaphore_hdr_size = Os_taskqueue_hdr_size + 4<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
Sub Os_semaphore_create_at(memptr As Word , Byval Tokens As Byte) As Word<br />
Os_mem_clear Memptr , Os_semaphore_hdr_size<br />
<br />
Setbyte Memptr , Os_semaphore_hdr_tokensize , Tokens<br />
Setbyte Memptr , Os_semaphore_hdr_tokencount , Tokens<br />
End Sub<br />
<br />
'(*****f* Semaphore/Os_semaphore_create ****************************************<br />
* DESCRIPTION<br />
* Creates a new semaphore object.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_kill<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_create(byval Tokens As Byte) As Word<br />
'(<br />
* SOURCE<br />
')<br />
Local Semaphore As Word<br />
<br />
Semaphore = Malloc(os_semaphore_hdr_size)<br />
If Semaphore = 0 Then<br />
Os_semaphore_create = 0<br />
Exit Function<br />
End If<br />
Os_semaphore_create_at Semaphore , Tokens<br />
<br />
Os_semaphore_create = Semaphore<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_kill ******************************************<br />
* DESCRIPTION<br />
* Kills a semaphore object<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_create<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_kill(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
If Semaphore <> 0 Then<br />
Os_semaphore_flush Semaphore<br />
Free Semaphore<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_aquire ****************************************<br />
* DESCRIPTION<br />
* Tries to aquire a semaphore token. If there are no tokens left, the queue<br />
* mode action takes place.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_release, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Function Os_semaphore_aquire(byref Semaphore As Word , Byval Queuemode As Word) As Byte<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
'cli<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
' no tokens left, put in waiting list and suspend<br />
' Os_sched_priority_inheritance(os_task_active)<br />
Select Case Queuemode<br />
Case Os_queuemode_noblock:<br />
' return error<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
Case Os_queuemode_block:<br />
' suspend and wait to send a message<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_nowakeup , 0<br />
Case Else<br />
' suspend and wait to send a message or timeout<br />
Os_task_suspendmode Os_task_active , Os_task_suspend_timersingleshot , Queuemode<br />
End Select<br />
Os_sched_taskqueue_insert Semaphore , Os_task_active<br />
'Os_exit_critical<br />
Os_task_suspend Os_task_active<br />
<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
If Tokencount = 0 Then<br />
Os_exit_critical<br />
Os_semaphore_aquire = False<br />
Exit Function<br />
End If<br />
End If<br />
<br />
Decr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
Os_exit_critical<br />
Os_semaphore_aquire = True<br />
End Function<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_release ***************************************<br />
* DESCRIPTION<br />
* Releases a semaphore token. The releasing task must not have aquired it<br />
* before.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_flush<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_release(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokencount As Byte<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
Os_enter_critical<br />
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
<br />
If Tokencount < Tokensize Then<br />
' release a token<br />
Incr Tokencount<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount<br />
' let any waiting task aquire the released token<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task <> 0 Then<br />
Os_task_event Task<br />
End If<br />
Else<br />
Os_exit_critical<br />
End If<br />
End Sub<br />
'******** **********************************************************************<br />
<br />
<br />
<br />
'(*****f* Semaphore/Os_semaphore_flush *****************************************<br />
* DESCRIPTION<br />
* Releases all semaphore tokens. The releasing task must not have aquired it<br />
* before. All tasks from the waiting list are put to ready state.<br />
* SEE ALSO<br />
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_release<br />
* DECLARATION<br />
')<br />
Sub Os_semaphore_flush(byref Semaphore As Word)<br />
'(<br />
* SOURCE<br />
')<br />
Local Tokensize As Byte<br />
Local Task As Word<br />
<br />
' reset tokens<br />
Os_enter_critical<br />
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)<br />
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokensize<br />
Os_exit_critical<br />
<br />
' resume all tasks waiting in the list<br />
Do<br />
Os_enter_critical<br />
Task = Os_sched_taskqueue_remove(semaphore)<br />
Os_exit_critical<br />
If Task = 0 Then Exit Do<br />
Os_task_event Task<br />
Loop<br />
End Sub<br />
'******** **********************************************************************</pre><br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=Static_Memory_Allocation&diff=335Static Memory Allocation2023-11-04T16:17:06Z<p>Mat: /* Samples */</p>
<hr />
<div>== Index ==<br />
* [[TLSF Dynamic Memory Allocation|''[OOP 1a/3] Dynamic Memory Allocation using TLSF'']]<br />
* [OOP 1b/3] Static Memory Allocation<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
In most applications, dynamic memory allocation is not needed, instead, it is allocated one time (either at the compile-stage or during the boot process) and unchanged during run-time.<br />
Where dynamic memory is not needed, but to maintain compatibility, this static allocation library is used.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable). <br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_static.inc'' into the folder.<br />
<br />
Include the library:<br />
<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_start = ### ' mandatory<br />
$include "inc\os_malloc_static.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.1). It can be found by examining the compile report (with setting ''Options->Compiler->Output->Show internal variables'' enabled), the last variable (with the highest adress) plus the byte size is the adress of the first unused byte (refer to [[TLSF_Dynamic_Memory_Allocation#Compiler_report_example|TLSF Dynamic Memory Allocation]]).<br />
<br />
<br />
=== Managing memory ===<br />
The library maintains a pointer to the first free memory position, memory requests are served in a consecutive order (incrementing the pointer by the requested size). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library. Releasing memory for use in further requests is not possible.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 1</pre><br />
Define this constant (value doesn't matter) to keep track of the remaining available memory (see below).<br />
<br />
<br />
<pre>Dim Available_memory As Word/Dword</pre><br />
If the constant ''Os_task_dbg_metrics'' is defined, the remaining memory is available to the application via this variable.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Allocate a block of memory. Cannot be freed. Returns 0 if not enough free memory left to serve the request.<br />
<br />
== Samples ==<br />
=== M32 (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 1911<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1911<br />
Free Memory: 1399<br />
adr1: 104 adr2: 616<br />
Free Memory: 1354<br />
</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7742<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_static.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1<br />
<br />
Memoryblock2 = Malloc(48)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396350<br />
Alloc 123, free memory: 8396227<br />
Address 1: 8257<br />
Alloc 48, free memory: 8396179<br />
Address 2: 8380<br />
Alloc 10000, free memory: 8378608<br />
Address 3: 16384<br />
Alloc 12345, free memory: 8366263<br />
Address 4: 26384</pre><br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=334TLSF Dynamic Memory Allocation2023-11-04T16:11:00Z<p>Mat: /* Samples */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left".<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the remaining free memory size could be found. In this example, there are 7477 bytes left.<br />
<pre><br />
Report : tlsf-xmem-sample<br />
Date : 11-03-2023<br />
Time : 02:51:23<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.8.6<br />
Processor : XM128A1U<br />
SRAM : 2000 hex<br />
EEPROM : 800 hex<br />
ROMSIZE : 22000 hex<br />
<br />
ROMIMAGE : 324A hex -> Will fit into ROM<br />
ROMIMAGE : 12874 dec<br />
FLASH USED : 9 %<br />
UART1 : 0.01 % ERROR<br />
XTAL : 32000000 Hz<br />
<br />
XRAM : 800000 hex<br />
Stack start : 3FFF hex<br />
Stack size : 80 hex<br />
S-Stacksize : 80 hex<br />
S-Stackstart : 3F80 hex<br />
Framesize : 80 hex<br />
Framestart : 3E80 hex<br />
Space left : 7477 dec <------<br />
<br />
LCD DB7 : PORTB.7<br />
LCD DB6 : PORTB.6<br />
LCD DB5 : PORTB.5<br />
LCD DB4 : PORTB.4<br />
LCD E : PORTB.3<br />
LCD RS : PORTB.2<br />
LCD mode : 4 bit<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
DACA0 Word 0318 792<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
=== M32 internal SRAM (runs in Simulator) ===<br />
<pre>$regfile = "m32def.dat"<br />
$crystal = 1000000<br />
$hwstack = 32<br />
$swstack = 32<br />
$framesize = 64<br />
$baud = 9600<br />
Config Submode = New<br />
<br />
<br />
<br />
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)<br />
Const Os_mem_size_free = 1810<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
Dim Memoryblock1 As Word<br />
Dim Memoryblock2 As Word<br />
<br />
Print "Free Memory: " ; Available_memory<br />
Memoryblock1 = Malloc(512)<br />
Memoryblock2 = Malloc(45)<br />
Print "adr1: " ; Memoryblock1 ; " adr2: " ; Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
Free Memoryblock1<br />
Free Memoryblock2<br />
Print "Free Memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Free Memory: 1804<br />
adr1: 210 adr2: 726<br />
Free Memory: 1236<br />
Free Memory: 1804</pre><br />
<br />
=== ATXMega128A1-Xplained (8Mb ext. SRAM) ===<br />
<pre>$regfile = "xm128a1udef.dat"<br />
$crystal = 32000000<br />
$baud = 115200<br />
$hwstack = 128<br />
$swstack = 128<br />
$framesize = 128<br />
Config Submode = New<br />
<br />
Config Osc = Disabled , 32mhzosc = Enabled<br />
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1<br />
<br />
Config Com1 = 115200 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8<br />
Open "COM1:" For Binary As #1<br />
<br />
<br />
<br />
' =========== XRAM ============<br />
'the XPLAINED has a 64 MBit SDRAM which is 8 MByte, it is connected in 3 port, 4 bit databus mode<br />
'in the PDF of the SDRAM you can see it is connected as 16 Meg x 4. Refreshcount is 4K and the row address is A0-A11, column addressing is A0-A9<br />
$xramsize = 8388608 ' 8 MByte<br />
Config Xram = 3port , Sdbus = 4 , Sdcol = 10 , Sdcas = 3 , Sdrow = 12 , Refresh = 500 , Initdelay = 3200 , Modedelay = 2 , Rowcycledelay = 7 , Rowprechargedelay = 7 , Wrdelay = 1 , Esrdelay = 7 , Rowcoldelay = 7 , Modesel3 = Sdram , Adrsize3 = 8m , Baseadr3 = &H0000<br />
'the config above will set the port registers correct. it will also wait for Ebi_cs3_ctrlb.7<br />
<br />
<br />
<br />
' =========== MEMORY ALLOCATOR ============<br />
' Set size of the free memory pool manually (examine compile report, -> "Space Left")<br />
Const Os_mem_size_free = 7476 ' activated debug info needs an additional byte<br />
<br />
Const Os_task_dbg_metrics = 1 ' constant needs to be defined if we want to see the available memory (value doesn't matter)<br />
Const Debug_level_tlsf = 0 ' print out additional debug info, possible values: 0 (no info), 1, 2 and 3 (full info)<br />
<br />
' include needed libraries<br />
$include "..\..\src\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected<br />
$include "..\..\src\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,<br />
' we need to override the default includes<br />
$include "..\..\src\inc\os_malloc_tlsf.inc"<br />
<br />
<br />
<br />
' =========== DEMO ============<br />
Dim Memoryblock1 As Dword<br />
Dim Memoryblock2 As Dword<br />
Dim Memoryblock3 As Dword<br />
Dim Memoryblock4 As Dword<br />
Dim Blocksize As Dword<br />
<br />
Print "Start, free memory: " ; Available_memory<br />
<br />
' allocate some smaller blocks (will be served from internal sram first)<br />
Memoryblock1 = Malloc(123)<br />
Blocksize = Os_malloc_blocksize(memoryblock1)<br />
Print "Alloc 123, free memory: " ; Available_memory<br />
Print "Address 1: " ; Memoryblock1 ; " Size: " ; Blocksize<br />
<br />
Memoryblock2 = Malloc(48)<br />
Blocksize = Os_malloc_blocksize(memoryblock2)<br />
Print "Alloc 48, free memory: " ; Available_memory<br />
Print "Address 2: " ; Memoryblock2 ; " Size: " ; Blocksize<br />
<br />
' allocate bigger blocks to force allocation from external memory<br />
Memoryblock3 = Malloc(10000)<br />
Blocksize = Os_malloc_blocksize(memoryblock3)<br />
Print "Alloc 10000, free memory: " ; Available_memory<br />
Print "Address 3: " ; Memoryblock3 ; " Size: " ; Blocksize<br />
<br />
Memoryblock4 = Malloc(12345)<br />
Blocksize = Os_malloc_blocksize(memoryblock4)<br />
Print "Alloc 12345, free memory: " ; Available_memory<br />
Print "Address 4: " ; Memoryblock4 ; " Size: " ; Blocksize<br />
<br />
' free up allocated memory<br />
Free Memoryblock1<br />
Print "Freed block 1, free memory: " ; Available_memory<br />
<br />
Free Memoryblock2<br />
Print "Freed block 2, free memory: " ; Available_memory<br />
<br />
Free Memoryblock3<br />
Print "Freed block 3, free memory: " ; Available_memory<br />
<br />
Free Memoryblock4<br />
Print "Freed block 4, free memory: " ; Available_memory<br />
<br />
End</pre><br />
<br />
Output:<br />
<pre>Start, free memory: 8396068<br />
Alloc 123, free memory: 8395932<br />
Address 1: 8531 Size: 128<br />
Alloc 48, free memory: 8395876<br />
Address 2: 8667 Size: 48<br />
Alloc 10000, free memory: 8385868<br />
Address 3: 16392 Size: 10000<br />
Alloc 12345, free memory: 8373512<br />
Address 4: 26400 Size: 12348<br />
Freed block 1, free memory: 8373640<br />
Freed block 2, free memory: 8373704<br />
Freed block 3, free memory: 8383704<br />
Freed block 4, free memory: 8396068</pre><br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=333TLSF Dynamic Memory Allocation2023-11-04T16:00:12Z<p>Mat: /* How to use it */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_size = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.6). It can be found by examining the compile report, fill in the value from "Space Left".<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the remaining free memory size could be found. In this example, there are 7477 bytes left.<br />
<pre><br />
Report : tlsf-xmem-sample<br />
Date : 11-03-2023<br />
Time : 02:51:23<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.8.6<br />
Processor : XM128A1U<br />
SRAM : 2000 hex<br />
EEPROM : 800 hex<br />
ROMSIZE : 22000 hex<br />
<br />
ROMIMAGE : 324A hex -> Will fit into ROM<br />
ROMIMAGE : 12874 dec<br />
FLASH USED : 9 %<br />
UART1 : 0.01 % ERROR<br />
XTAL : 32000000 Hz<br />
<br />
XRAM : 800000 hex<br />
Stack start : 3FFF hex<br />
Stack size : 80 hex<br />
S-Stacksize : 80 hex<br />
S-Stackstart : 3F80 hex<br />
Framesize : 80 hex<br />
Framestart : 3E80 hex<br />
Space left : 7477 dec <------<br />
<br />
LCD DB7 : PORTB.7<br />
LCD DB6 : PORTB.6<br />
LCD DB5 : PORTB.5<br />
LCD DB4 : PORTB.4<br />
LCD E : PORTB.3<br />
LCD RS : PORTB.2<br />
LCD mode : 4 bit<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
DACA0 Word 0318 792<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
WIP<br />
<br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=TLSF_Dynamic_Memory_Allocation&diff=332TLSF Dynamic Memory Allocation2023-11-04T15:55:33Z<p>Mat: /* Interface description */</p>
<hr />
<div>== Index ==<br />
* [OOP 1a/3] Dynamic Memory Allocation using TLSF<br />
* [[Static Memory Allocation|''[OOP 1b/3] Static Memory Allocation'']]<br />
* [[Object Oriented Programming in Bascom|''[OOP 2/3] Object Oriented Programming in Bascom'']]<br />
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']]<br />
<br />
<br />
== Overview ==<br />
[http://www.gii.upv.es/tlsf/ TLSF] (Two-Level Segregated Fit) is a dynamic memory allocator suited for real-time systems.<br />
It is a good-fit algorithm with a bound response time O(1) for any number of managed memory blocks.<br />
<br />
Free blocks of memory are organized in lists each containing blocks of a specific size range. Requested blocks are searched in the list with sizes equal or greater the requested size, remaining memory is split up if neccessary and sorted back into the list. Freed blocks are merged with neighboring free blocks and also put back into the list.<br />
<br />
The remaining memory from the end of global declared variables to the start of the stack spaces and also external memory (Xram), if available, is managed by the library (configurable).<br />
<br />
<br />
== How to use it ==<br />
=== Setup ===<br />
<br />
Create an ''inc'' subfolder to your project and copy the files ''os_common.inc'', ''os_memory.inc'' and ''os_malloc_tlsf.inc'' into the folder.<br />
<br />
Include the library:<br />
<pre>' any const option must be placed above the file include<br />
Const Os_mem_free_start = ### ' mandatory<br />
$include "inc\os_malloc_tlsf.inc"</pre><br />
<br />
The start adress of the free memory area can not be determined automatically (Bascom-AVR 2.0.8.1). It can be found by examining the compile report (with setting ''Options->Compiler->Output->Show internal variables'' enabled), the last variable (with the highest adress) plus the byte size is the adress of the first unused byte.<br />
<br />
==== Compiler report example ====<br />
Here is a (shortened) example of a compiler report, and where the address could be found. In this example, the RAM start address should be set to at least 169 dec.<br />
<pre><br />
Report : chronos<br />
Date : 05-20-2013<br />
Time : 01:54:06<br />
<br />
Compiler : BASCOM-AVR LIBRARY V 2.0.7.6<br />
Processor : M32<br />
SRAM : 800 hex<br />
EEPROM : 400 hex<br />
ROMSIZE : 8000 hex<br />
<br />
...<br />
<br />
--------------------------------------------------------------------------------<br />
Variable Type Address(hex) Address(dec)<br />
--------------------------------------------------------------------------------<br />
<br />
...<br />
<br />
OS_CRITICAL_NESTING_LEVEL Byte 0060 96<br />
OS_EVENT_TASK Word 0061 97<br />
MALLOC_FREE_POINTER Word 0063 99<br />
OS_SCHED_RUNNING_WEIGHT Byte 0065 101<br />
OS_SCHED_LIST_HEAD Word 0066 102<br />
OS_SCHED_PREEMPTED_TASK Word 0068 104<br />
OS_TASK_ACTIVE Word 006A 106<br />
OS_TASK_TEMPBYTE_ISR Byte 006C 108<br />
OS_TASK_TEMPWORD_ISR Word 006D 109<br />
OS_TASK_TEMPWORD2_ISR Word 006F 111<br />
OS_TIMER_SYSTEMTIME Dword 0071 113<br />
OS_TIMER_LISTHEAD Word 0075 117<br />
OS_TIMER_NEXT_EVENT Word 0077 119<br />
OS_TIMER_ELAPSED_TIME Word 0079 121<br />
OS_TIMER_ENABLE_KERNEL Byte 007B 123<br />
OS_INT_TABLE Word (20) 007C 124<br />
TASKOBJECT Word 00A4 164<br />
TASK_1_COUNTER Byte 00A6 166<br />
TASK_2_COUNTER Byte 00A7 167<br />
___LCDCOUNTER Byte 00A8 168 <---<br />
<br />
--------------------------------------------------------------------------------<br />
Constant Value<br />
--------------------------------------------------------------------------------<br />
SREG &H3F<br />
SPH &H3E<br />
SPL &H3D<br />
<br />
...</pre><br />
<br />
=== Managing memory ===<br />
<br />
The library works with memory adresses. If a memory request of given size can be served (consecutive block of free memory has been found), the adress of the start of the block is returned. For a size request bigger than what is managed by the algorithm, as fallback a simple search for a block of suitable size through the last list will be performed (O(n)). If the request can't be fullfilled, the value 0 is returned. The application has to check for that return value and react accordingly, it is also responsible not to write outside of the requested block of memory. Allocated memory is NOT cleared (set to 0) by the library.<br />
<br />
With no external memory, 16 bit adress width (Word variable type) is used, with external memory 24 bits are used (DWord type).<br />
<pre>Dim Myblockofmemory As [Word/Dword]<br />
Myblockofmemory = Malloc([Size])<br />
If Myblockofmemory = 0 Then<br />
' memory allocation error handling (not enough free memory)<br />
End If</pre><br />
<br />
<br />
When the block is no longer needed, it must be returned to the free memory pool by calling:<br />
<pre>Free Myblockofmemory</pre><br />
<br />
<br />
== Interface description ==<br />
The following constants have to be defined prior to including the library. The values shown here are default values if the constant is not defined by the user.<br />
<br />
<pre>Const Os_mem_free_size = [Compile Report->Space Left]</pre><br />
<pre>Const Os_mem_free_start = [&H200/with XRAM: &H2000]</pre><br />
MANDATORY parameter, as described in [[#Setup|Setup]]. For reasons, it has also default values.<br />
Set either one, the other will be calculated. Setting ''Os_mem_free_size'' is the recommended method.<br />
<br />
<br />
<pre>Const Os_tlsf_fli_offset = 4</pre><br />
Start of first level block size ranges in powers of 2. Size = 2 ^ (Os_tlsf_fli_offset + 1); with a default value of 4, first level sizes start at 32.<br />
<br />
<br />
<pre>Const Os_tlsf_max_fli = [10/with XRAM: 12]</pre><br />
End of first level block size ranges. Size = 2 ^ Os_tlsf_max_fli; defaults without XRAM to 1024, with XRAM to 4096.<br />
<br />
<br />
<pre>Const Os_tlsf_max_log2_sli = 3</pre><br />
Log2 of Second level range count (max. 3). Since the search process is speed up by using bitmaps, this value should match the platform's word width. For 8-Bit AVR it is 2 ^ 3 = 8.<br />
<br />
<br />
These 3 parameters result in the following block size ranges (default values, no XRAM):<br />
<pre>First level | Second level<br />
(+4+1) | 0 1 2 3 4 5 6 7<br />
--------------------------------------------------------------------------<br />
0 | 32 | 0 4 8 12 16 20 24 28<br />
1 | 64 | 32 36 40 44 48 52 56 60<br />
2 | 128 | 64 72 80 88 96 104 112 120<br />
3 | 256 | 128 144 160 176 192 208 224 240<br />
4 | 512 | 256 288 320 352 384 416 448 480<br />
5 | 1024 | 512 576 640 704 768 832 896 960</pre><br />
Index 0 is not used, Index 1 is a list containing blocks up to a size of 4 Bytes, Index 2 list has sizes from 4 to 8 Bytes, Index 8 contains 16-32 Byte blocks, ...<br />
<br />
<br />
<pre>Const Debug_level_tlsf = 0</pre><br />
Set debug output verbosity, 0 = no debug, 1 = Print out available memory each Malloc/Free, 2 = Print out memory pool overview, 3 = Print additional info during Malloc/Free. If debug outputs are activated, option ''Os_task_debug_metrics'' is also declared.<br />
<br />
<br />
<pre>Const Os_task_dbg_metrics = 0</pre><br />
Declare Const (value doesn't matter) to enable tracking of available memory. If activated, a variable will be created:<br />
<pre>#if Os_big_ram = False<br />
Dim Available_memory As Word<br />
#else<br />
Dim Available_memory As Dword<br />
#endif</pre><br />
<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Malloc(byval Size As Word) As Word<br />
#else<br />
Function Malloc(byval Size As Dword) As Dword<br />
#endif</pre><br />
Request a block of memory of given size. Returns the starting memory adress or zero if not enough free memory left.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Sub Free(byref Block As Word)<br />
#else<br />
Sub Free(byref Block As Dword)<br />
#endif</pre><br />
Returns a previously allocated block to the free memory pool.<br />
<br />
<br />
<pre>#if Os_big_ram = False<br />
Function Os_malloc_blocksize(byref Block As Word) As Word<br />
#else<br />
Function Os_malloc_blocksize(byref Block As Dword) As Dword<br />
#endif</pre><br />
Returns the size of a block.<br />
<br />
<br />
<pre>Sub Os_malloc_clear()</pre><br />
Empties all lists, resets the allocator<br />
<br />
<br />
<pre>Sub Os_malloc_init()</pre><br />
Initializes the allocator. This is done when the library is included, in standard applications, it is not needed to call. However, to reset the allocator, use ''Os_malloc_clear'' before calling ''Os_malloc_init''.<br />
<br />
== Samples ==<br />
WIP<br />
<br />
<br />
== Download ==<br />
<br />
[[Bascom OOP & Precompiler Download]]</div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=331Hypercube Evolution 3D Printer2021-05-17T21:10:04Z<p>Mat: /* Housing */</p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
Machining the MDF back-, cover- and base plates<br />
<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Backplate_milling_cable_ducts.jpg|frameless|Milling the cable ducts]]<br />
[[Datei:Backplate_milled_cable_ducts.jpg|frameless|Finished cable ducts]]<br />
<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
== Bed assembly ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]] <br />
[[Datei:Bed_cablechain.jpg|frameless|Cable chain attached]]<br />
<br />
<br />
[[Datei:Bed_plate_drilled.jpg|frameless|Machined holes in naked aluminium bed plate]]<br />
[[Datei:Bed_screw_and_spring.jpg|frameless|Screws and silicone springs mounted]]<br />
[[Datei:Bed_heater_thermal_fuse_applied.jpg|frameless|Applied the 230V/800W heating mat and a manually resettable thermal fuse]]<br />
[[Datei:Applying_bed_layers.jpg|frameless|Applying the adhesives to the flex printing surface]]<br />
<br />
[[Datei:Bed_flex_plate_positioning_brackets.jpg|frameless|Print surface positioning brackets cut from aluminium L-beams]]<br />
[[Datei:Bed_flex_plate_positioning_brackets_mounted.jpg|frameless|Positioning brackets mounted]]<br />
[[Datei:Bed_print_surface_fr4.jpg|frameless|FR4 print surface]]<br />
<br />
<br />
To hold the insulation material beneath the bed in place, I've made this support plate:<br />
<br />
[[Datei:Bed_insulation_material_support.jpg|frameless|Cutted beams and drilled support plate]] <br />
[[Datei:Bed_insulation_support_top.jpg|frameless|Support plate sitting on top of the bed frame]]<br />
[[Datei:Bed_insulation_support_bottom.jpg|frameless|Support plate seen from below]]<br />
[[Datei:Bed_insulation.jpg|frameless|Bed insulation material, the sides are covered in tape to prevent fluff]]<br />
<br />
<br />
[[Datei:Bed_completed_assembly.jpg|frameless|Bed assembly completed]] <br />
<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]<br />
[[Datei:Z_linear_screw.jpg|frameless|Z axis linear screws mounted]]<br />
<br />
<br />
== X-, Y-Axis ==<br />
[[Datei:Linear_rails.jpg|frameless|Linear rails cleaned, greased and ready to be mounted]]<br />
[[Datei:Linear_rails_detail.jpg|frameless|Applied some screw lock]]<br />
<br />
<br />
[[Datei:Y_carriage_on_rail.jpg|frameless|Y carriage on the rail]]<br />
[[Datei:Y_optical_endstop.jpg|frameless|Optical end stop on the Y-axis]]<br />
<br />
<br />
[[Datei:X_carriage_front.jpg|frameless|X carriage and hot end front view]]<br />
[[Datei:X_carriage_below.jpg|frameless|X carriage and hot end from below]]<br />
<br />
<br />
[[Datei:Belt_idler.jpg|frameless|Belt idler mounted]]</div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=330Hypercube Evolution 3D Printer2021-05-17T18:00:07Z<p>Mat: </p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
Machining the MDF back-, cover- and base plates<br />
<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Backplate_milling_cable_ducts.jpg|frameless|Milling the cable ducts]]<br />
[[Datei:Backplate_milled_cable_ducts.jpg|frameless|Finished cable ducts]]<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
<br />
== Bed assembly ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]] <br />
[[Datei:Bed_cablechain.jpg|frameless|Cable chain attached]]<br />
<br />
<br />
[[Datei:Bed_plate_drilled.jpg|frameless|Machined holes in naked aluminium bed plate]]<br />
[[Datei:Bed_screw_and_spring.jpg|frameless|Screws and silicone springs mounted]]<br />
[[Datei:Bed_heater_thermal_fuse_applied.jpg|frameless|Applied the 230V/800W heating mat and a manually resettable thermal fuse]]<br />
[[Datei:Applying_bed_layers.jpg|frameless|Applying the adhesives to the flex printing surface]]<br />
<br />
[[Datei:Bed_flex_plate_positioning_brackets.jpg|frameless|Print surface positioning brackets cut from aluminium L-beams]]<br />
[[Datei:Bed_flex_plate_positioning_brackets_mounted.jpg|frameless|Positioning brackets mounted]]<br />
[[Datei:Bed_print_surface_fr4.jpg|frameless|FR4 print surface]]<br />
<br />
<br />
To hold the insulation material beneath the bed in place, I've made this support plate:<br />
<br />
[[Datei:Bed_insulation_material_support.jpg|frameless|Cutted beams and drilled support plate]] <br />
[[Datei:Bed_insulation_support_top.jpg|frameless|Support plate sitting on top of the bed frame]]<br />
[[Datei:Bed_insulation_support_bottom.jpg|frameless|Support plate seen from below]]<br />
[[Datei:Bed_insulation.jpg|frameless|Bed insulation material, the sides are covered in tape to prevent fluff]]<br />
<br />
<br />
[[Datei:Bed_completed_assembly.jpg|frameless|Bed assembly completed]] <br />
<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]<br />
[[Datei:Z_linear_screw.jpg|frameless|Z axis linear screws mounted]]<br />
<br />
<br />
== X-, Y-Axis ==<br />
[[Datei:Linear_rails.jpg|frameless|Linear rails cleaned, greased and ready to be mounted]]<br />
[[Datei:Linear_rails_detail.jpg|frameless|Applied some screw lock]]<br />
<br />
<br />
[[Datei:Y_carriage_on_rail.jpg|frameless|Y carriage on the rail]]<br />
[[Datei:Y_optical_endstop.jpg|frameless|Optical end stop on the Y-axis]]<br />
<br />
<br />
[[Datei:X_carriage_front.jpg|frameless|X carriage and hot end front view]]<br />
[[Datei:X_carriage_below.jpg|frameless|X carriage and hot end from below]]<br />
<br />
<br />
[[Datei:Belt_idler.jpg|frameless|Belt idler mounted]]</div>Mathttps://mat.midlight.eu/index.php?title=Datei:Linear_rails_detail.jpg&diff=329Datei:Linear rails detail.jpg2021-05-17T17:14:34Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Linear_rails.jpg&diff=328Datei:Linear rails.jpg2021-05-17T17:14:09Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_plate_drilled.jpg&diff=327Datei:Bed plate drilled.jpg2021-05-17T17:13:32Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_heater_thermal_fuse_applied.jpg&diff=326Datei:Bed heater thermal fuse applied.jpg2021-05-17T17:12:50Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Y_optical_endstop.jpg&diff=324Datei:Y optical endstop.jpg2021-05-17T15:38:04Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Applying_bed_layers.jpg&diff=323Datei:Applying bed layers.jpg2021-05-17T15:37:25Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_screw_and_spring.jpg&diff=322Datei:Bed screw and spring.jpg2021-05-17T15:36:30Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Y_carriage_on_rail.jpg&diff=321Datei:Y carriage on rail.jpg2021-05-17T15:35:34Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Belt_idler.jpg&diff=320Datei:Belt idler.jpg2021-05-17T15:34:37Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:X_carriage_front.jpg&diff=319Datei:X carriage front.jpg2021-05-17T15:30:06Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:X_carriage_below.jpg&diff=318Datei:X carriage below.jpg2021-05-17T15:29:33Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Backplate_milled_cable_ducts.jpg&diff=317Datei:Backplate milled cable ducts.jpg2021-05-17T15:28:53Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_print_surface_fr4.jpg&diff=316Datei:Bed print surface fr4.jpg2021-05-17T15:27:42Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_flex_plate_positioning_brackets.jpg&diff=315Datei:Bed flex plate positioning brackets.jpg2021-05-17T15:26:49Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_flex_plate_positioning_brackets_mounted.jpg&diff=314Datei:Bed flex plate positioning brackets mounted.jpg2021-05-17T15:26:16Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_insulation_material_support.jpg&diff=313Datei:Bed insulation material support.jpg2021-05-17T15:25:45Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_insulation_support_top.jpg&diff=312Datei:Bed insulation support top.jpg2021-05-17T15:25:03Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_insulation_support_bottom.jpg&diff=311Datei:Bed insulation support bottom.jpg2021-05-17T15:23:26Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Z_linear_screw.jpg&diff=310Datei:Z linear screw.jpg2021-05-17T15:22:40Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_insulation.jpg&diff=309Datei:Bed insulation.jpg2021-05-17T15:21:37Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_completed_assembly.jpg&diff=308Datei:Bed completed assembly.jpg2021-05-17T15:20:27Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Backplate_milling_cable_ducts.jpg&diff=307Datei:Backplate milling cable ducts.jpg2021-05-17T15:19:44Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Bed_cablechain.jpg&diff=306Datei:Bed cablechain.jpg2021-05-17T15:19:03Z<p>Mat: </p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Datei:Hevo_improvised_tap.jpg&diff=304Datei:Hevo improvised tap.jpg2021-05-17T14:47:33Z<p>Mat: Mat lud eine neue Version von Datei:Hevo improvised tap.jpg hoch</p>
<hr />
<div></div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=303Hypercube Evolution 3D Printer2021-05-10T12:54:14Z<p>Mat: </p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]] <br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]</div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=302Hypercube Evolution 3D Printer2021-04-21T13:54:23Z<p>Mat: </p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]]<br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]</div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=301Hypercube Evolution 3D Printer2021-04-21T12:01:58Z<p>Mat: </p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]]<br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]<br />
edit</div>Mathttps://mat.midlight.eu/index.php?title=Hypercube_Evolution_3D_Printer&diff=300Hypercube Evolution 3D Printer2021-04-20T12:46:34Z<p>Mat: Die Seite wurde neu angelegt: „== Parts overview == Parts overview == Frame == Frame == Dampened Feet ==…“</p>
<hr />
<div>== Parts overview ==<br />
[[Datei:Hevo materials overview.jpg|frameless|Parts overview]]<br />
<br />
== Frame ==<br />
[[Datei:Hevo frame.jpg|frameless|Frame]]<br />
<br />
== Dampened Feet ==<br />
To mount the damper feets, the extrusions need to be tapped. Because the standard 3-flute hand tap does not bite in the first few millimeters, I improvised a 1-flute tap to start the thread and finished it with the 3-flute tap.<br />
[[Datei:Hevo improvised tap.jpg|frameless|Improvised tap]]<br />
[[Datei:Hevo improvised tap 2.jpg|frameless|Improvised tap closeup]]<br />
<br />
[[Datei:Hevo damper feet.jpg|frameless|Dampened feet]]<br />
<br />
== Housing ==<br />
[[Datei:Hevo mdf machined.jpg|frameless|MDF plates after machining]]<br />
[[Datei:Hevo frame housing.jpg|frameless|Housing plates mounted on the frame]]<br />
<br />
== Z-Axis ==<br />
[[Datei:Hevo bed frame.jpg|frameless|Bed frame assembled]]<br />
[[Datei:Hevo z linear guides.jpg|frameless|Z linear guides and bed frame mounted]]</div>Mathttps://mat.midlight.eu/index.php?title=Datei:Hevo_bed_frame.jpg&diff=299Datei:Hevo bed frame.jpg2021-04-20T12:40:25Z<p>Mat: </p>
<hr />
<div></div>Mat