68 of 208
ZBasic Language Reference 60 ZX Microcontroller Family 3.4.1 System Queues The console I/O routines like Console.Read() and Console.Write() use the input and output queues associated with Com1. Because of their special use in this manner, the queues associated with Com1 are called system queues. The values Register.RxQueue and Register.TxQueue give the address of the system input queue and system output queue respectively. The function CByteArray() can be used to convert these values to a reference to a Byte array allowing them to be passed to the queue- related routines. The example code below shows how to determine if there is any data available in the system input queue using this technique. If StatusQueue(CByteArray(Register.RxQueue)) Then b = Console.Read() End If The system queues are initially set to be small pre-defined queues. If your code opens Com1, the queues that you provide with the OpenCom() invocation become the system queues until you subsequently close Com1. At that time, the system queues will revert to the pre-defined queues. Of the two pre-defined queues, the output queue is the smallest, having space for only a few data bytes. Because of this, if you send a fairly long string to the output queue using Console.Write() your application will experience a delay until the string’s characters can all be transferred to the output queue. If this causes a problem in your application you can define a larger queue to be used as the output queue. The example code below does so while retaining the pre-defined input queue. Private sysOutQueue(1 to 40) as Byte Call OpenQueue(sysOutQueue, SizeOf(sysOutQueue)) Call CloseCom(1, 0, 0) Call OpenCom(1, Option.Com1Speed, _ CByteArray(Register.RxQueue), sysOutQueue) For native mode devices (e.g. the ZX-24n) you can specify the default sizes for the system queues using the directives Option TxQueueSize and Option RxQueueSize. 3.5 Multitasking The concept of multitasking is a very powerful one although it can be, at first, somewhat confusing. In a single task system, there is only one program and it runs continuously until it is terminated. During the course of execution of the program it may perform several different functions, e.g. checking the keyboard to see if a character is available, updating the display unit, etc. The fundamental idea behind multitasking is that these activities are divided into related groups, each of which is called a task. Each task executes for a period of time and then the next task executes. There are two basic types of multitasking: cooperative and preemptive. In cooperative multitasking it is left up to each task to determine when to suspend itself and allow the next task to run. In preemptive multitasking a part of the operating system called a task scheduler determines, generally, how long a task is allowed to run before switching to the next task. The multitasking in the ZX microcontrollers is, fundamentally, preemptive multitasking. The task scheduler switches tasks on each RTC tick (approximately 1.95mS). A task can, however, lock itself to avoid (with some exceptions) giving up execution. This capability should be used sparingly because it defeats the equitable sharing of processing resources. It is not necessary, of course, to structure your program to utilize the multitasking capabilities. If there is only one task defined (the subroutine Main()) the task scheduler will simply allow it to run continuously. If your application might benefit from being divided into tasks, it is simple to define them. Each task has two core elements: a task main routine and a task stack. The task main routine is the (usually parameterless) subroutine with which the task begins execution. The task stack is a portion of RAM set aside exclusively for the task. The example below shows how to define a task stack and how to activate a second task.