<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://smartbox.benryves.com/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Benryves</id>
	<title>Smart Box - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://smartbox.benryves.com/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Benryves"/>
	<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/wiki/Special:Contributions/Benryves"/>
	<updated>2026-07-15T22:03:39Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-180.svg&amp;diff=298</id>
		<title>File:DIN-5-Socket-180.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-180.svg&amp;diff=298"/>
		<updated>2025-02-21T12:35:51Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Benryves uploaded a new version of File:DIN-5-Socket-180.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;5-pin DIN socket (180°)&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-240.svg&amp;diff=297</id>
		<title>File:DIN-5-Socket-240.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-240.svg&amp;diff=297"/>
		<updated>2025-02-21T12:35:43Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Tutorial&amp;diff=295</id>
		<title>Tutorial</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Tutorial&amp;diff=295"/>
		<updated>2023-11-18T06:28:39Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Disconnecting Smart Box from the host computer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About Smart Move ==&lt;br /&gt;
&lt;br /&gt;
Smart Move is the control program designed for use with Smart Box.&lt;br /&gt;
With Smart Move you can create your own programs to control models and other devices connected to Smart Box.&lt;br /&gt;
Smart Move includes special commands to control the motors and other outputs.&lt;br /&gt;
It also includes commands to detect the signals received from any sensors you have connected to Smart Box.&lt;br /&gt;
The sensors are the ''inputs'' to the system.&lt;br /&gt;
The motors, lamps or other similar devices are the ''outputs''.&lt;br /&gt;
The outputs can be switched on and off depending on the state of the inputs.&lt;br /&gt;
For example, a lamp can be switched on when a light sensor detects a fall in light level, or a buzzer can be switched on when a temperature sensor detects a predetermined temperature.&lt;br /&gt;
&lt;br /&gt;
== Data capture ==&lt;br /&gt;
&lt;br /&gt;
Smart Move also includes commands which allow you to save readings from the sensors to disk.&lt;br /&gt;
These sensor readings can then be analysed using other programs such as a spreadsheet.&lt;br /&gt;
The readings can also be stored when certain conditions are met.&lt;br /&gt;
For example the temperature during the hours of darkness could be taken by recording the values from the temperature sensor only when the light sensor reading is below a certain level.&lt;br /&gt;
This feature allows sophisticated data capture experiments to be designed.&lt;br /&gt;
&lt;br /&gt;
== Procedures ==&lt;br /&gt;
&lt;br /&gt;
Each Smart Move program you create will consist of at least one ''procedure''.&lt;br /&gt;
A procedure is a sequence of instructions which are grouped together and given a name.&lt;br /&gt;
You then simply refer to the procedure name to make your program execute the complete sequence of instructions.&lt;br /&gt;
A typical Smart Move program will be broken into a number of small procedures, each procedure performing a precise part of the overall program.&lt;br /&gt;
Breaking the program down into procedures allows each one to be tested independently.&lt;br /&gt;
Each procedure can be used any number of times within any other procedure.&lt;br /&gt;
You will usually have one main procedure which uses a number of sub procedures.&lt;br /&gt;
&lt;br /&gt;
== Presumed Knowledge ==&lt;br /&gt;
&lt;br /&gt;
This guide presumes no prior knowledge of Smart Box, Smart Move or control technology.&lt;br /&gt;
However, the guide does assume that you know how to perform certain basic tasks with your computer. You should know how to:&lt;br /&gt;
&lt;br /&gt;
* Use the keyboard&lt;br /&gt;
* Copy disks, programs and documents&lt;br /&gt;
* Perform simple editing tasks, including deleting text&lt;br /&gt;
&lt;br /&gt;
If you need to learn more about these basic tasks, consult the handbooks and disks that came with your computer.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
The following sections of this guide take you through creating your first program, and provide a number of example procedures.&lt;br /&gt;
The language commands are detailed in full in the [[reference]] section.&lt;br /&gt;
&lt;br /&gt;
Make sure that Smart Box and your computer are correctly connected and switched on (see [[Setting up#Setting up procedure|Smart Box setting up procedure]]), then load the Smart Move software.&lt;br /&gt;
&lt;br /&gt;
[[File:Tutorial-fig01.png|frame|none|fig. 1]]&lt;br /&gt;
&lt;br /&gt;
Once loaded you will see the main Smart Move screen as shown in fig. 1.&lt;br /&gt;
The top of the screen is the monitor window.&lt;br /&gt;
This area of the screen displays the current state of the various inputs and outputs.&lt;br /&gt;
The bottom section of the screen is the text window.&lt;br /&gt;
You use this window to type in commands.&lt;br /&gt;
Any values you ask Smart Move to print will also be displayed here.&lt;br /&gt;
&lt;br /&gt;
* [[Switching outputs on and off]]&lt;br /&gt;
* [[Procedures]]&lt;br /&gt;
* [[Repeating actions]]&lt;br /&gt;
* [[Variables]]&lt;br /&gt;
* [[Procedures within procedures]]&lt;br /&gt;
* [[Saving procedures]]&lt;br /&gt;
* [[Loading procedures]]&lt;br /&gt;
* [[Using sensors]]&lt;br /&gt;
* [[Making decisions]]&lt;br /&gt;
* [[Data capture]]&lt;br /&gt;
* [[Average]]&lt;br /&gt;
* [[Controlling a motor]]&lt;br /&gt;
* [[Varying power and pulsing outputs]]&lt;br /&gt;
* [[Printing procedures]]&lt;br /&gt;
&lt;br /&gt;
== Disconnecting Smart Box from the host computer ==&lt;br /&gt;
&lt;br /&gt;
If you close the Smart Move application using &amp;quot;Disconnect&amp;quot; instead of &amp;quot;Quit&amp;quot; then the procedures in the Smart Box will continue running.&lt;br /&gt;
The disconnect command differs according to the host computer platform:&lt;br /&gt;
&lt;br /&gt;
* PC: Press Shift+F10&lt;br /&gt;
* Apple Macintosh: Select File→Disconnect from the Smart Move application menu.&lt;br /&gt;
* Acorn Archimedes: Select Disconnect from the menu displayed when the menu button is clicked over the program icon on the Icon bar.&lt;br /&gt;
* BBC Micro / Master: Press Shift+''f''&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Disconnecting closes the Smart Move program while allowing the procedures in the Smart Box to continue running.&lt;br /&gt;
The host computer can then be disconnected from the Smart Box.&lt;br /&gt;
When the connection is remade and the Smart Move program is run, any procedures in Smart Box will be reloaded into Smart Move.&lt;br /&gt;
If power is disconnected from the Smart Box, its memory is cleared, so save procedures before disconnecting.&lt;br /&gt;
&lt;br /&gt;
This section has introduced the basic principles involved in creating a Smart Move program.&lt;br /&gt;
The [[reference]] section gives full details of each command.&lt;br /&gt;
Whenever you write a Smart Move program try to create a number of procedures, each doing a specific task, rather than one large program doing everything.&lt;br /&gt;
In this way you will be able to test the individual parts of your program and identify the errors more easily.&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Tutorial&amp;diff=294</id>
		<title>Tutorial</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Tutorial&amp;diff=294"/>
		<updated>2023-11-18T06:27:30Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Disconnecting Smart Box from the host computer */ Corrected the BBC Micro keyboard shortcut&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About Smart Move ==&lt;br /&gt;
&lt;br /&gt;
Smart Move is the control program designed for use with Smart Box.&lt;br /&gt;
With Smart Move you can create your own programs to control models and other devices connected to Smart Box.&lt;br /&gt;
Smart Move includes special commands to control the motors and other outputs.&lt;br /&gt;
It also includes commands to detect the signals received from any sensors you have connected to Smart Box.&lt;br /&gt;
The sensors are the ''inputs'' to the system.&lt;br /&gt;
The motors, lamps or other similar devices are the ''outputs''.&lt;br /&gt;
The outputs can be switched on and off depending on the state of the inputs.&lt;br /&gt;
For example, a lamp can be switched on when a light sensor detects a fall in light level, or a buzzer can be switched on when a temperature sensor detects a predetermined temperature.&lt;br /&gt;
&lt;br /&gt;
== Data capture ==&lt;br /&gt;
&lt;br /&gt;
Smart Move also includes commands which allow you to save readings from the sensors to disk.&lt;br /&gt;
These sensor readings can then be analysed using other programs such as a spreadsheet.&lt;br /&gt;
The readings can also be stored when certain conditions are met.&lt;br /&gt;
For example the temperature during the hours of darkness could be taken by recording the values from the temperature sensor only when the light sensor reading is below a certain level.&lt;br /&gt;
This feature allows sophisticated data capture experiments to be designed.&lt;br /&gt;
&lt;br /&gt;
== Procedures ==&lt;br /&gt;
&lt;br /&gt;
Each Smart Move program you create will consist of at least one ''procedure''.&lt;br /&gt;
A procedure is a sequence of instructions which are grouped together and given a name.&lt;br /&gt;
You then simply refer to the procedure name to make your program execute the complete sequence of instructions.&lt;br /&gt;
A typical Smart Move program will be broken into a number of small procedures, each procedure performing a precise part of the overall program.&lt;br /&gt;
Breaking the program down into procedures allows each one to be tested independently.&lt;br /&gt;
Each procedure can be used any number of times within any other procedure.&lt;br /&gt;
You will usually have one main procedure which uses a number of sub procedures.&lt;br /&gt;
&lt;br /&gt;
== Presumed Knowledge ==&lt;br /&gt;
&lt;br /&gt;
This guide presumes no prior knowledge of Smart Box, Smart Move or control technology.&lt;br /&gt;
However, the guide does assume that you know how to perform certain basic tasks with your computer. You should know how to:&lt;br /&gt;
&lt;br /&gt;
* Use the keyboard&lt;br /&gt;
* Copy disks, programs and documents&lt;br /&gt;
* Perform simple editing tasks, including deleting text&lt;br /&gt;
&lt;br /&gt;
If you need to learn more about these basic tasks, consult the handbooks and disks that came with your computer.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
The following sections of this guide take you through creating your first program, and provide a number of example procedures.&lt;br /&gt;
The language commands are detailed in full in the [[reference]] section.&lt;br /&gt;
&lt;br /&gt;
Make sure that Smart Box and your computer are correctly connected and switched on (see [[Setting up#Setting up procedure|Smart Box setting up procedure]]), then load the Smart Move software.&lt;br /&gt;
&lt;br /&gt;
[[File:Tutorial-fig01.png|frame|none|fig. 1]]&lt;br /&gt;
&lt;br /&gt;
Once loaded you will see the main Smart Move screen as shown in fig. 1.&lt;br /&gt;
The top of the screen is the monitor window.&lt;br /&gt;
This area of the screen displays the current state of the various inputs and outputs.&lt;br /&gt;
The bottom section of the screen is the text window.&lt;br /&gt;
You use this window to type in commands.&lt;br /&gt;
Any values you ask Smart Move to print will also be displayed here.&lt;br /&gt;
&lt;br /&gt;
* [[Switching outputs on and off]]&lt;br /&gt;
* [[Procedures]]&lt;br /&gt;
* [[Repeating actions]]&lt;br /&gt;
* [[Variables]]&lt;br /&gt;
* [[Procedures within procedures]]&lt;br /&gt;
* [[Saving procedures]]&lt;br /&gt;
* [[Loading procedures]]&lt;br /&gt;
* [[Using sensors]]&lt;br /&gt;
* [[Making decisions]]&lt;br /&gt;
* [[Data capture]]&lt;br /&gt;
* [[Average]]&lt;br /&gt;
* [[Controlling a motor]]&lt;br /&gt;
* [[Varying power and pulsing outputs]]&lt;br /&gt;
* [[Printing procedures]]&lt;br /&gt;
&lt;br /&gt;
== Disconnecting Smart Box from the host computer ==&lt;br /&gt;
&lt;br /&gt;
If you close the Smart Move application using &amp;quot;Disconnect&amp;quot; instead of &amp;quot;Quit&amp;quot; then the procedures in the Smart Box will continue running.&lt;br /&gt;
The disconnect command differs according to the host computer platform:&lt;br /&gt;
&lt;br /&gt;
* PC: Press Shift+F10&lt;br /&gt;
* Apple Macintosh: Select File→Disconnect from the Smart Move application menu.&lt;br /&gt;
* Acorn Archimedes: Select Disconnect from the menu displayed when the menu button is clicked over the program icon on the Icon bar.&lt;br /&gt;
* BBC Micro / Master: Press Shift+''f''&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pressing Shift+F10 closes the Smart Move program while allowing the procedures in the Smart Box to continue running.&lt;br /&gt;
The host computer can then be disconnected from the Smart Box.&lt;br /&gt;
When the connection is remade and the Smart Move program is run, any procedures in Smart Box will be reloaded into Smart Move.&lt;br /&gt;
If power is disconnected from the Smart Box, its memory is cleared, so save procedures before disconnecting.&lt;br /&gt;
&lt;br /&gt;
This section has introduced the basic principles involved in creating a Smart Move program.&lt;br /&gt;
The [[reference]] section gives full details of each command.&lt;br /&gt;
Whenever you write a Smart Move program try to create a number of procedures, each doing a specific task, rather than one large program doing everything.&lt;br /&gt;
In this way you will be able to test the individual parts of your program and identify the errors more easily.&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Internal_Hardware&amp;diff=293</id>
		<title>Internal Hardware</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Internal_Hardware&amp;diff=293"/>
		<updated>2023-11-13T01:19:43Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added component names to list&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The following documentation is from studying the inside of an SB-01 SmartBox version 2 issue 1.1. Other revisions are known to have different internal components.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Part number   !! Name                                          !! Datasheet&lt;br /&gt;
|-&lt;br /&gt;
| CPU       || R65C02P3      || RC65C02 Microprocessor                        || [[Media:Datasheet-CPU-R65C02.pdf|R65C02]]&lt;br /&gt;
|-&lt;br /&gt;
| RAM       || TC55257BPI10L || 32,768 x 8 bit static RAM                     || [[Media:Datasheet-RAM-TC55257BPI.pdf|TC55257BPI]]&lt;br /&gt;
|-&lt;br /&gt;
| ROM       || NMC27C64Q     || 8,192 x 8 bit CMOS EPROM                      || [[Media:Datasheet-ROM-NMC27C64.pdf|NMC27C64]]&lt;br /&gt;
|-&lt;br /&gt;
| VIA       || R6522AP       || R6522 Versatile Interface Adapter             || [[Media:Datasheet-VIA-R6522.pdf|R6522]]&lt;br /&gt;
|-&lt;br /&gt;
| ACIA      || EF68B50P      || Asynchronous Communications Interface Adapter || [[Media:Datasheet-ACIA-EF6850.pdf|EF6850]]&lt;br /&gt;
|-&lt;br /&gt;
| ADC       || D7002C        || 12-bit Binary A/D Converter                   || [[Media:Datasheet-ADC-D7002.pdf|µD7002]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Internal_Hardware&amp;diff=292</id>
		<title>Internal Hardware</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Internal_Hardware&amp;diff=292"/>
		<updated>2023-11-12T23:46:58Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added list of parts and datasheets&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The following documentation is from studying the inside of an SB-01 SmartBox version 2 issue 1.1. Other revisions are known to have different internal components.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Part number !! Datasheet&lt;br /&gt;
|-&lt;br /&gt;
| CPU       || R65C02P3      || [[Media:Datasheet-CPU-R65C02.pdf|R65C02]]&lt;br /&gt;
|-&lt;br /&gt;
| RAM       || TC55257BPI10L || [[Media:Datasheet-RAM-TC55257BPI.pdf|TC55257BPI]]&lt;br /&gt;
|-&lt;br /&gt;
| ROM       || NMC27C64Q     || [[Media:Datasheet-ROM-NMC27C64.pdf|NMC27C64]]&lt;br /&gt;
|-&lt;br /&gt;
| VIA       || R6522AP       || [[Media:Datasheet-VIA-R6522.pdf|R6522]]&lt;br /&gt;
|-&lt;br /&gt;
| ACIA      || EF68B50P      || [[Media:Datasheet-ACIA-EF6850.pdf|EF6850]]&lt;br /&gt;
|-&lt;br /&gt;
| ADC       || D7002C        || [[Media:Datasheet-ADC-D7002.pdf|µD7002]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ADC-D7002.pdf&amp;diff=291</id>
		<title>File:Datasheet-ADC-D7002.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ADC-D7002.pdf&amp;diff=291"/>
		<updated>2023-11-12T23:46:17Z</updated>

		<summary type="html">&lt;p&gt;Benryves: µD7002C 12-bit binary A/D converter datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
µD7002C 12-bit binary A/D converter datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ACIA-EF6850.pdf&amp;diff=290</id>
		<title>File:Datasheet-ACIA-EF6850.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ACIA-EF6850.pdf&amp;diff=290"/>
		<updated>2023-11-12T23:45:29Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Summary */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
EF6850 asynchronous communications interface adapter (ACIA) datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ACIA-EF6850.pdf&amp;diff=289</id>
		<title>File:Datasheet-ACIA-EF6850.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ACIA-EF6850.pdf&amp;diff=289"/>
		<updated>2023-11-12T23:45:18Z</updated>

		<summary type="html">&lt;p&gt;Benryves: EF6850 asynchronous communications interface adapter (ACIA)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
EF6850 asynchronous communications interface adapter (ACIA)&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-VIA-R6522.pdf&amp;diff=288</id>
		<title>File:Datasheet-VIA-R6522.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-VIA-R6522.pdf&amp;diff=288"/>
		<updated>2023-11-12T23:44:31Z</updated>

		<summary type="html">&lt;p&gt;Benryves: R6522 Versatile Interface Adapter (VIA) datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
R6522 Versatile Interface Adapter (VIA) datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ROM-NMC27C64.pdf&amp;diff=287</id>
		<title>File:Datasheet-ROM-NMC27C64.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-ROM-NMC27C64.pdf&amp;diff=287"/>
		<updated>2023-11-12T23:44:08Z</updated>

		<summary type="html">&lt;p&gt;Benryves: NMC27C64 65,536-Bit (8192 x 8) CMOS EPROM datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
NMC27C64 65,536-Bit (8192 x 8) CMOS EPROM datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-RAM-TC55257BPI.pdf&amp;diff=286</id>
		<title>File:Datasheet-RAM-TC55257BPI.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-RAM-TC55257BPI.pdf&amp;diff=286"/>
		<updated>2023-11-12T23:43:26Z</updated>

		<summary type="html">&lt;p&gt;Benryves: TC55257BPI/BFI/BSPI/BFTI/BTRI-10L 32,768 word x 8 bit static RAM datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
TC55257BPI/BFI/BSPI/BFTI/BTRI-10L 32,768 word x 8 bit static RAM datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-CPU-R65C02.pdf&amp;diff=285</id>
		<title>File:Datasheet-CPU-R65C02.pdf</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Datasheet-CPU-R65C02.pdf&amp;diff=285"/>
		<updated>2023-11-12T23:33:43Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Rockwell R65C02, R5C102 and R65C112 R65C00 Microprocessors datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Rockwell R65C02, R5C102 and R65C112 R65C00 Microprocessors datasheet&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Main_Page&amp;diff=284</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Main_Page&amp;diff=284"/>
		<updated>2023-11-12T23:26:26Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Technical Information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Smart Box User Guide ==&lt;br /&gt;
&lt;br /&gt;
Information has been adapted from the original ''Smart Box'' and ''Smart Move'' user manual, copyright Economatics (Education) Limited 1992.&lt;br /&gt;
&lt;br /&gt;
* [[Smart Box]] - A guide to using the Smart Box, including the Smart Box ''EV''.&lt;br /&gt;
* [[Tutorial]] - Tutorial for the Smart Move software.&lt;br /&gt;
* [[Reference]] - A general reference guide to Smart Move commands.&lt;br /&gt;
* [[Smart Sensors and Relay]] - Information sheets on individual sensors and input/output devices.&lt;br /&gt;
&lt;br /&gt;
== Technical Information ==&lt;br /&gt;
&lt;br /&gt;
* [[Serial Port]] - Pinout for the Smart Box's serial port.&lt;br /&gt;
* [[Analogue Sensor Ports]] - Pinout for the four analogue sensors.&lt;br /&gt;
* [[Internal Hardware]] - The chips that make the Smart Box work.&lt;br /&gt;
&lt;br /&gt;
The following pages have been adapted from information shared at https://github.com/Phipli/SmartBox/issues/&lt;br /&gt;
&lt;br /&gt;
* [[SmartBox JobList]] - A list of the available jobs (routines) provided by the Smart Box OS that can be invoked from the serial link.&lt;br /&gt;
* [[SmartBox OS]] - Information about the organisation of the operating system and development of additional jobs.&lt;br /&gt;
* [[SmartBox AlbertLink]] - Details of the AlbertLink job used by the Smart Move software.&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Serial_Port&amp;diff=283</id>
		<title>Serial Port</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Serial_Port&amp;diff=283"/>
		<updated>2023-11-12T04:22:57Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added content for serial port documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Pinout ==&lt;br /&gt;
&lt;br /&gt;
[[File:DIN-5-Socket-240.svg|frame|right|5-pin 240° DIN socket]]&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Pin !! Function&lt;br /&gt;
|-&lt;br /&gt;
| 1 || CTS&lt;br /&gt;
|-&lt;br /&gt;
| 2 || GND (0V)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || RTS&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD&lt;br /&gt;
|-&lt;br /&gt;
| 5 || RXD&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The pin numbers and functions are given for the Smart Box as a DCE, so when connected to a computer (which is a DTE) you should the connect pins directly, i.e. TXD on the Smart Box connects to TXD on the PC, RXD on the Smart Box connects to RXD on the PC etc.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== PC ==&lt;br /&gt;
&lt;br /&gt;
[[File:DE-9F.svg|frame|right|DE-9F serial connector]]&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Pin !! Function&lt;br /&gt;
|-&lt;br /&gt;
| 1 ||&lt;br /&gt;
|-&lt;br /&gt;
| 2 || RXD&lt;br /&gt;
|-&lt;br /&gt;
| 3 || TXD&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND (0V)&lt;br /&gt;
|-&lt;br /&gt;
| 6 ||&lt;br /&gt;
|-&lt;br /&gt;
| 7 || RTS&lt;br /&gt;
|-&lt;br /&gt;
| 8 || CTS&lt;br /&gt;
|-&lt;br /&gt;
| 9 ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:DE-9F.svg&amp;diff=282</id>
		<title>File:DE-9F.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:DE-9F.svg&amp;diff=282"/>
		<updated>2023-11-12T04:21:25Z</updated>

		<summary type="html">&lt;p&gt;Benryves: DE-9F connector&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
DE-9F connector&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Analogue_Sensor_Ports&amp;diff=280</id>
		<title>Analogue Sensor Ports</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Analogue_Sensor_Ports&amp;diff=280"/>
		<updated>2023-11-12T03:31:40Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added content for analogue sensor ports documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Pinout ==&lt;br /&gt;
&lt;br /&gt;
[[File:DIN-5-Socket-180.svg|frame|right|5-pin 180° DIN socket]]&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Pin !! Function&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Analogue signal input&lt;br /&gt;
|-&lt;br /&gt;
| 2 || GND (0V)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; (+5V)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Sense signal output&lt;br /&gt;
|-&lt;br /&gt;
| 5 || V&amp;lt;sub&amp;gt;ref&amp;lt;/sub&amp;gt; (+2.55V)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Analogue input ==&lt;br /&gt;
&lt;br /&gt;
The expected analogue input on pin 1 is between 0V and V&amp;lt;sub&amp;gt;ref&amp;lt;/sub&amp;gt; (+2.55V) which will correspond to a reading of between 0 and 255 in the SmartMove software.&lt;br /&gt;
&lt;br /&gt;
Each unit on the analogue input can therefore be said to correspond to 1/100V.&lt;br /&gt;
&lt;br /&gt;
== Sense output ==&lt;br /&gt;
&lt;br /&gt;
To identify the different types of Smart Sensor software on the Smart Box will drive the &amp;quot;Sense&amp;quot; pin 4 high approximately once per second. When the sense pin is driven high sensors should return a voltage corresponding to their sensor type to the Smart Box's analogue input. This can be implemented in your own sensors using an analogue multiplexer/demultiplexer (such as the 4053 IC) to switch between the sensor's output voltage when sense is low and the sensor's ID voltage when sense is high, e.g. produced by a trim pot between 0V and V&amp;lt;sub&amp;gt;ref&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Sensor IDs ===&lt;br /&gt;
&lt;br /&gt;
There are 32 possible sensor IDs. The ADC reading mentioned in the table below is the minimum value that will cause the Smart Move software to identify a particular sensor, for example a humidity sensor can be identified with an ADC reading between 157 and 164.&lt;br /&gt;
&lt;br /&gt;
Note that Smart Move 1.16 and Smart Move 1.18 have some different names for some sensors. If a space is left blank in the table then the sensor is treated as present but is labelled &amp;quot;SENSORA&amp;quot; to &amp;quot;SENSORD&amp;quot; instead of being renamed according to the sensor type.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!ADC!!Smart Move 1.16!!Smart Move 1.18&lt;br /&gt;
|-&lt;br /&gt;
|0||''No sensor''||''No sensor''&lt;br /&gt;
|-&lt;br /&gt;
|5||mT|| &lt;br /&gt;
|-&lt;br /&gt;
|13||mm||Temp&lt;br /&gt;
|-&lt;br /&gt;
|21||No sensor||No sensor&lt;br /&gt;
|-&lt;br /&gt;
|29||No sensor||No sensor&lt;br /&gt;
|-&lt;br /&gt;
|37||Temp||Volts&lt;br /&gt;
|-&lt;br /&gt;
|45||Temp||Temp&lt;br /&gt;
|-&lt;br /&gt;
|53||mA||Volts&lt;br /&gt;
|-&lt;br /&gt;
|61||Sound||Temp&lt;br /&gt;
|-&lt;br /&gt;
|69||Sound||Sound&lt;br /&gt;
|-&lt;br /&gt;
|77||Sound||PH&lt;br /&gt;
|-&lt;br /&gt;
|85||Pulse|| &lt;br /&gt;
|-&lt;br /&gt;
|93||Position||Position&lt;br /&gt;
|-&lt;br /&gt;
|101|| || &lt;br /&gt;
|-&lt;br /&gt;
|109|| || &lt;br /&gt;
|-&lt;br /&gt;
|117|| || &lt;br /&gt;
|-&lt;br /&gt;
|125||Light||Light&lt;br /&gt;
|-&lt;br /&gt;
|133|| || &lt;br /&gt;
|-&lt;br /&gt;
|141|| || &lt;br /&gt;
|-&lt;br /&gt;
|149|| || &lt;br /&gt;
|-&lt;br /&gt;
|157||Humidity||Humidity&lt;br /&gt;
|-&lt;br /&gt;
|165|| ||Sound&lt;br /&gt;
|-&lt;br /&gt;
|173|| ||Light&lt;br /&gt;
|-&lt;br /&gt;
|181||Pressure||Sound&lt;br /&gt;
|-&lt;br /&gt;
|189|| || &lt;br /&gt;
|-&lt;br /&gt;
|197||PH||Atmos&lt;br /&gt;
|-&lt;br /&gt;
|205|| ||Light&lt;br /&gt;
|-&lt;br /&gt;
|213|| ||User&lt;br /&gt;
|-&lt;br /&gt;
|221||Adaptor||Adaptor&lt;br /&gt;
|-&lt;br /&gt;
|229||Lux||Temp&lt;br /&gt;
|-&lt;br /&gt;
|237||Volts||LGate&lt;br /&gt;
|-&lt;br /&gt;
|245||Wind|| &lt;br /&gt;
|-&lt;br /&gt;
|253||Temp||Temp&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-180.svg&amp;diff=279</id>
		<title>File:DIN-5-Socket-180.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:DIN-5-Socket-180.svg&amp;diff=279"/>
		<updated>2023-11-12T03:03:25Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;5-pin DIN socket (180°)&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_AlbertLink&amp;diff=278</id>
		<title>SmartBox AlbertLink</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_AlbertLink&amp;diff=278"/>
		<updated>2023-11-12T00:15:54Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Converted plaintext documentation to markdown&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AlbertLink Link Protocol (Release 11)&lt;br /&gt;
&lt;br /&gt;
== Startup ==&lt;br /&gt;
&lt;br /&gt;
After AL has been downloaded and called the first thing back will be a engine release number (one byte), and known setup flags (one byte) which you should check that the &amp;quot;new&amp;quot; flags you want are set. If the engine number is wrong or the setup flags you want aren't set then send a 0 and AlbertLink will quit itself, else send ANYTHING but a 0. Then send your setup flags (one byte), which will engage the various &amp;quot;new&amp;quot; options.&lt;br /&gt;
&lt;br /&gt;
ie:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
| send jobcode for AlbertLink || &amp;amp;rarr; ||&lt;br /&gt;
|-&lt;br /&gt;
|                             || &amp;amp;larr; || engine number&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
|                             || &amp;amp;larr; || known setup flags&lt;br /&gt;
|-&lt;br /&gt;
| flag for continue or not    || &amp;amp;rarr; ||&lt;br /&gt;
|-&lt;br /&gt;
| setup flags wanted          || &amp;amp;larr; ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Setup Flags ==&lt;br /&gt;
&lt;br /&gt;
The 8 setup flags are setup as such:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! bit !! if set&lt;br /&gt;
|-&lt;br /&gt;
| 0   ||  use new trace system&lt;br /&gt;
|-&lt;br /&gt;
| 1   ||  enable procedure/label change checking&lt;br /&gt;
|-&lt;br /&gt;
| 2   ||  enable custom commands&lt;br /&gt;
|-&lt;br /&gt;
| 3   ||  enable prompt&lt;br /&gt;
|-&lt;br /&gt;
| 4   ||  shit computer&lt;br /&gt;
|-&lt;br /&gt;
| 5   ||  enable &amp;quot;get&amp;quot; line count&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Idle ==&lt;br /&gt;
&lt;br /&gt;
At this point the system is idling, both ends are waiting to originate a &amp;quot;event&amp;quot; or receive one.&lt;br /&gt;
&lt;br /&gt;
The remote end should check the serial port as often as possible, if it detects a byte then it should read it, check it is in range, and transmit a 0 (byte) back, then it should do one of the following &amp;quot;event&amp;quot; types according to the byte it received:&lt;br /&gt;
&lt;br /&gt;
=== File ===&lt;br /&gt;
&lt;br /&gt;
1 +byte +string&lt;br /&gt;
&lt;br /&gt;
* byte = channel reference (1 to 10)&lt;br /&gt;
* string = file name&lt;br /&gt;
* the file should then be opened and a FileBack event made or a Error event, to signal a error&lt;br /&gt;
&lt;br /&gt;
=== Close ===&lt;br /&gt;
&lt;br /&gt;
2 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = channel reference&lt;br /&gt;
&lt;br /&gt;
=== Store === &lt;br /&gt;
&lt;br /&gt;
3 +byte +data (terminated by NUL)&lt;br /&gt;
&lt;br /&gt;
* byte = channel reference&lt;br /&gt;
* data = data to put to file&lt;br /&gt;
&lt;br /&gt;
=== Trace ===&lt;br /&gt;
&lt;br /&gt;
==== Old ====&lt;br /&gt;
&lt;br /&gt;
4 +string&lt;br /&gt;
&lt;br /&gt;
* string = string to print&lt;br /&gt;
&lt;br /&gt;
==== New ====&lt;br /&gt;
&lt;br /&gt;
4 +string +double byte&lt;br /&gt;
&lt;br /&gt;
* string = procedure name&lt;br /&gt;
* double byte = line number&lt;br /&gt;
&lt;br /&gt;
Return: byte&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0, no stepping&lt;br /&gt;
*: 1, wait for tracecont&lt;br /&gt;
&lt;br /&gt;
=== Print ===&lt;br /&gt;
&lt;br /&gt;
5 +[characters terminated by NUL, CR expand to CRLF]&lt;br /&gt;
&lt;br /&gt;
* do not CRLF after this, keep cursor position&lt;br /&gt;
&lt;br /&gt;
=== Error ===&lt;br /&gt;
&lt;br /&gt;
6 +string +string2 +string3       (to change)&lt;br /&gt;
&lt;br /&gt;
* string = procedure name (blank if from cmd line)&lt;br /&gt;
* string2 = error&lt;br /&gt;
* string3 = line containing error (blank if from cmd line)&lt;br /&gt;
&lt;br /&gt;
=== Ask ===&lt;br /&gt;
&lt;br /&gt;
7 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = 'S', 'N' or 'T' (input type)&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
* use AskBack to return input string&lt;br /&gt;
&lt;br /&gt;
=== Inkey ===&lt;br /&gt;
&lt;br /&gt;
8&lt;br /&gt;
&lt;br /&gt;
Return: byte&lt;br /&gt;
* 0 -&amp;gt; no key&lt;br /&gt;
* #0 -&amp;gt; key value&lt;br /&gt;
&lt;br /&gt;
=== Cmd ===&lt;br /&gt;
&lt;br /&gt;
9&lt;br /&gt;
&lt;br /&gt;
* this means that command mode is ready&lt;br /&gt;
&lt;br /&gt;
=== Build ===&lt;br /&gt;
&lt;br /&gt;
10 +string&lt;br /&gt;
&lt;br /&gt;
* string = proc to edit&lt;br /&gt;
&lt;br /&gt;
=== Edit ===&lt;br /&gt;
&lt;br /&gt;
11 +string&lt;br /&gt;
&lt;br /&gt;
* string = proc to edit&lt;br /&gt;
&lt;br /&gt;
=== Quit ===&lt;br /&gt;
&lt;br /&gt;
12&lt;br /&gt;
&lt;br /&gt;
* user has typed QUIT, use Quit to quit system&lt;br /&gt;
&lt;br /&gt;
=== TraceFl ===&lt;br /&gt;
&lt;br /&gt;
13 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = trace flag (0 -&amp;gt; off, #0 -&amp;gt; on)&lt;br /&gt;
&lt;br /&gt;
=== Load ===&lt;br /&gt;
&lt;br /&gt;
14 +string&lt;br /&gt;
&lt;br /&gt;
* string = filename&lt;br /&gt;
* use Put&lt;br /&gt;
* use Error for filing errors&lt;br /&gt;
* string can be blank&lt;br /&gt;
&lt;br /&gt;
=== Save ===&lt;br /&gt;
&lt;br /&gt;
15 +string +string2&lt;br /&gt;
&lt;br /&gt;
* string = filename&lt;br /&gt;
* string2 = procedure to save (or blank for all)&lt;br /&gt;
* use Error for filing errors&lt;br /&gt;
* use Get/List to get procedures&lt;br /&gt;
&lt;br /&gt;
=== Control ===&lt;br /&gt;
&lt;br /&gt;
16 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = control parameter, if out of range, return 0 and then use Error&lt;br /&gt;
&lt;br /&gt;
Return: byte&lt;br /&gt;
* byte = value of control option&lt;br /&gt;
&lt;br /&gt;
=== Rtc ===&lt;br /&gt;
&lt;br /&gt;
17&lt;br /&gt;
&lt;br /&gt;
Return: byte +byte2 +byte3 +byte4&lt;br /&gt;
* byte = hours&lt;br /&gt;
* byte2 = minutes&lt;br /&gt;
* byte3 = seconds&lt;br /&gt;
* byte4 = centiseconds&lt;br /&gt;
&lt;br /&gt;
Cause Error AFTER returning 0 for all if not supported.&lt;br /&gt;
&lt;br /&gt;
=== Printer ===&lt;br /&gt;
&lt;br /&gt;
18&lt;br /&gt;
&lt;br /&gt;
* as Print&lt;br /&gt;
&lt;br /&gt;
=== Altered ===&lt;br /&gt;
&lt;br /&gt;
19 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: bit 0 : procedure list changed&lt;br /&gt;
*: bit 1 : labels changed&lt;br /&gt;
&lt;br /&gt;
when you get this you should use List and/or ReadLabels to update yourself&lt;br /&gt;
&lt;br /&gt;
=== Custom ===&lt;br /&gt;
&lt;br /&gt;
20 +data (NUL terminated)&lt;br /&gt;
&lt;br /&gt;
=== Custom2 ===&lt;br /&gt;
&lt;br /&gt;
21 +data (NUL terminated)&lt;br /&gt;
&lt;br /&gt;
=== Customfn ===&lt;br /&gt;
&lt;br /&gt;
22 +string&lt;br /&gt;
&lt;br /&gt;
Return : byte&lt;br /&gt;
* byte = fn value&lt;br /&gt;
&lt;br /&gt;
=== Prompt ===&lt;br /&gt;
&lt;br /&gt;
23 +string&lt;br /&gt;
&lt;br /&gt;
* string = prompt string&lt;br /&gt;
&lt;br /&gt;
NB: &amp;quot;string&amp;quot; is a group of characters terminated by CR.&lt;br /&gt;
&lt;br /&gt;
Any other codes should be ignored.&lt;br /&gt;
&lt;br /&gt;
== Remote &amp;quot;events&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
The remote end also has &amp;quot;events&amp;quot; which it can originate, to start an event, transmit the &amp;quot;event&amp;quot; code and wait until you receive a 0 (byte), ignore all other bytes received (the remote end has priority) and then you can transmit any other bytes needed:&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
1 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = setup byte&lt;br /&gt;
* as startup setup flag&lt;br /&gt;
&lt;br /&gt;
=== List ===&lt;br /&gt;
&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
Return  [string .....] until string is blank&lt;br /&gt;
&lt;br /&gt;
* this returns a list of procedures terminated by a blank&lt;br /&gt;
&lt;br /&gt;
=== NameCode ===&lt;br /&gt;
&lt;br /&gt;
3 +string&lt;br /&gt;
&lt;br /&gt;
* '''NB''' doesn't return normal link 0 acknowledge.&lt;br /&gt;
* Emulates the normal OS NameCode, quiting AlbertLink if the remote doesn't try to check for AlbertLink and going out of sleep if it does try for AlbertLink&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
4 +byte +string&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0 : do not use labels&lt;br /&gt;
*: 1 : use labels&lt;br /&gt;
*: 255 : use LBLS setting&lt;br /&gt;
&lt;br /&gt;
==== Old ====&lt;br /&gt;
&lt;br /&gt;
Return byte:&lt;br /&gt;
* 0 &amp;amp;rarr; no such procedure else procedure terminated by 0ffh&lt;br /&gt;
&lt;br /&gt;
==== New ====&lt;br /&gt;
&lt;br /&gt;
Return:&lt;br /&gt;
* byte = 0 -&amp;gt; no such procedure&lt;br /&gt;
* 1 +byte2 +byte3 +procedure &amp;amp;rarr; procedure found&lt;br /&gt;
&lt;br /&gt;
byte2/3 is number of lines in procedure&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
&lt;br /&gt;
5 +string +[procedure strings terminated by 0ffh]&lt;br /&gt;
&lt;br /&gt;
Return: byte&lt;br /&gt;
* 0 &amp;amp;rarr; ok&lt;br /&gt;
* 1 &amp;amp;rarr; bad name&lt;br /&gt;
* 2 &amp;amp;rarr; no room&lt;br /&gt;
* 3 &amp;amp;rarr; bad data&lt;br /&gt;
&lt;br /&gt;
=== Escape ===&lt;br /&gt;
&lt;br /&gt;
6&lt;br /&gt;
&lt;br /&gt;
* causes escape condition&lt;br /&gt;
&lt;br /&gt;
=== Quit ===&lt;br /&gt;
&lt;br /&gt;
7&lt;br /&gt;
&lt;br /&gt;
* causes AlbertLink to quit&lt;br /&gt;
&lt;br /&gt;
=== Cmd ===&lt;br /&gt;
&lt;br /&gt;
8 +string&lt;br /&gt;
&lt;br /&gt;
* performs a raw command, use only after flagged cmd ready else any running procedure will be stopped&lt;br /&gt;
&lt;br /&gt;
=== GetPorts ===&lt;br /&gt;
&lt;br /&gt;
9&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
* byte = run mode (1 = running procedure)&lt;br /&gt;
* byte = inputs&lt;br /&gt;
* byte = outputs&lt;br /&gt;
* byte = motors&lt;br /&gt;
* byte = adc 1&lt;br /&gt;
* byte = adc 2&lt;br /&gt;
* byte = adc 3&lt;br /&gt;
* byte = adc 4&lt;br /&gt;
* byte = clock (hours)&lt;br /&gt;
* byte = clock&lt;br /&gt;
* byte = clock&lt;br /&gt;
* byte = clock (cs)&lt;br /&gt;
&lt;br /&gt;
=== GetPS ===&lt;br /&gt;
&lt;br /&gt;
10&lt;br /&gt;
&lt;br /&gt;
Return &lt;br /&gt;
* data as GetPorts&lt;br /&gt;
* byte = sensor a id&lt;br /&gt;
* byte = sensor b id&lt;br /&gt;
* byte = sensor c id&lt;br /&gt;
* byte = sensor d id&lt;br /&gt;
 &lt;br /&gt;
Perform once a second or so to re-check the sensors&lt;br /&gt;
&lt;br /&gt;
=== SteadyLine ===&lt;br /&gt;
&lt;br /&gt;
11 +byte +string&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0 : do not use labels&lt;br /&gt;
*: 1 : use labels&lt;br /&gt;
*: 255 : use LBLS setting&lt;br /&gt;
&lt;br /&gt;
Return string&lt;br /&gt;
* expanded line&lt;br /&gt;
 &lt;br /&gt;
use in editor to expanded abbreviated commands&lt;br /&gt;
&lt;br /&gt;
=== TraceFl ===&lt;br /&gt;
&lt;br /&gt;
12 +byte&lt;br /&gt;
* byte = new trace setting and causes Trace &amp;quot;events&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== SetPort ===&lt;br /&gt;
&lt;br /&gt;
13 +byte +byte2&lt;br /&gt;
&lt;br /&gt;
* byte = bits to set&lt;br /&gt;
* byte2 = bits to mask&lt;br /&gt;
&lt;br /&gt;
=== Error ===&lt;br /&gt;
&lt;br /&gt;
14 +string&lt;br /&gt;
&lt;br /&gt;
* string = error to cause&lt;br /&gt;
&lt;br /&gt;
=== Version ===&lt;br /&gt;
&lt;br /&gt;
15&lt;br /&gt;
&lt;br /&gt;
Return string&lt;br /&gt;
* version string&lt;br /&gt;
&lt;br /&gt;
=== Sleep ===&lt;br /&gt;
&lt;br /&gt;
16&lt;br /&gt;
&lt;br /&gt;
causes AlbertLink to sleep the remote link, waking up with a normal AlbertLink startup&lt;br /&gt;
&lt;br /&gt;
=== CheckSensors ===&lt;br /&gt;
&lt;br /&gt;
17&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
* byte = sensor a id&lt;br /&gt;
* byte = sensor b id&lt;br /&gt;
* byte = sensor c id&lt;br /&gt;
* byte = sensor d id&lt;br /&gt;
&lt;br /&gt;
=== AskBack ===&lt;br /&gt;
&lt;br /&gt;
18 +string&lt;br /&gt;
&lt;br /&gt;
* string = input line&lt;br /&gt;
&lt;br /&gt;
=== ReadLabels ===&lt;br /&gt;
&lt;br /&gt;
19&lt;br /&gt;
&lt;br /&gt;
Return [+string1 (+string2) .......]&lt;br /&gt;
* string1 = source label&lt;br /&gt;
* string2 = label (not sent if string1 is blank)&lt;br /&gt;
* string2 terminated by 0 for hard label and 128 for soft label&lt;br /&gt;
* end of list terminated by string1 being blank&lt;br /&gt;
&lt;br /&gt;
=== WriteLabel ===&lt;br /&gt;
&lt;br /&gt;
20 +string1 +string2 +byte&lt;br /&gt;
&lt;br /&gt;
* string1 = source label&lt;br /&gt;
* string2 = label&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0, hard label&lt;br /&gt;
*: 1, soft label&lt;br /&gt;
 &lt;br /&gt;
Return byte&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0, okay&lt;br /&gt;
*: 1, bad source label&lt;br /&gt;
*: 2, bad label&lt;br /&gt;
*: 3, label too long&lt;br /&gt;
*: 4, label exists as a procedure&lt;br /&gt;
*: 5, can't overwrite hard label with a soft label&lt;br /&gt;
&lt;br /&gt;
=== FreeMem ===&lt;br /&gt;
&lt;br /&gt;
21&lt;br /&gt;
&lt;br /&gt;
Return byte +byte2&lt;br /&gt;
* byte/2 = free memory&lt;br /&gt;
&lt;br /&gt;
=== TraceCont ===&lt;br /&gt;
&lt;br /&gt;
22&lt;br /&gt;
&lt;br /&gt;
This causes procedure execution to continue from a stopped trace&lt;br /&gt;
&lt;br /&gt;
=== Clock ===&lt;br /&gt;
&lt;br /&gt;
23 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0, stop clock&lt;br /&gt;
*: 1, start clock&lt;br /&gt;
*: 2, reset clock&lt;br /&gt;
&lt;br /&gt;
=== PromptBack ===&lt;br /&gt;
&lt;br /&gt;
24&lt;br /&gt;
&lt;br /&gt;
Send this when the user has clicked on the &amp;quot;prompt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== FileBack ===&lt;br /&gt;
&lt;br /&gt;
25 +byte&lt;br /&gt;
&lt;br /&gt;
* byte = flag&lt;br /&gt;
*: 0, cannot open file&lt;br /&gt;
*: 1, file opened&lt;br /&gt;
&lt;br /&gt;
Used in reply to File&lt;br /&gt;
&lt;br /&gt;
Print, Trace, SteadyLine, Get and Error surround any tokens with 1 to start token and 2 to finish token. Put and SteadyLine will remove them automatically before processing.&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_JobList&amp;diff=277</id>
		<title>SmartBox JobList</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_JobList&amp;diff=277"/>
		<updated>2023-11-11T23:52:03Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* OS_CALLOS */ Converted plaintext documentation to markdown&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;OS 4 - 14.02.00&lt;br /&gt;
&lt;br /&gt;
== 0 Blank ==&lt;br /&gt;
&lt;br /&gt;
Does nothing&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 0&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1 Version ==&lt;br /&gt;
&lt;br /&gt;
Read the operating system version number&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 1&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = version number - low byte&lt;br /&gt;
* Byte 1 = version number - high byte&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
Divide by 1000 to get the version number, eg. version 1.023 would be returned as 1023&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 2 Reset ==&lt;br /&gt;
&lt;br /&gt;
Reset Smart Box&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 2&lt;br /&gt;
* Byte 1 = 254 or 255&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
Reset SmartBox from the host micro. Sending a 254 performs a soft reset (same as pressing reset), sending 255 does a reset (clears battery back RAM). Before sending codes create a small delay while SmartBox resets various parts of hardware&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 3 NameCode ==&lt;br /&gt;
&lt;br /&gt;
To obtain the operating system call number where the name is known&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 3&lt;br /&gt;
* Byte 1 - n = ASCII characters of OS call name&lt;br /&gt;
* Byte n+1 = 13&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = Operating system call number&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4 CodeName ==&lt;br /&gt;
&lt;br /&gt;
To obtain the name associated with an operating system call&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 4&lt;br /&gt;
* Byte 1 = OS call number&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* StringCR = OS call name&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5 MultipleSetup ==&lt;br /&gt;
&lt;br /&gt;
To set the values that will be returned by MultipleRead&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 5&lt;br /&gt;
* Byte 1 = Analogue channel 1&lt;br /&gt;
* Byte 2 = Analogue channel 2&lt;br /&gt;
* Byte 3 = Analogue channel 3&lt;br /&gt;
* Byte 4 = Analogue channel 4&lt;br /&gt;
* Byte 5 = Digitial inputs&lt;br /&gt;
* Byte 6 = Digital outputs&lt;br /&gt;
* Byte 7 = Motor outputs&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This sets up the readings which will be returned when the call MultipleRead is made. If a byte = 1 the corresponding port will be returned, 0 = value not returned&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 6 MultipleRead ==&lt;br /&gt;
&lt;br /&gt;
Returns multiple readings as defined using MultipleSetup&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 6&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Bytes as defined by MultipleSetup&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This call returns readings from a number of ports as&lt;br /&gt;
		defined by MultipleSetup&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 7 MultipleServer ==&lt;br /&gt;
&lt;br /&gt;
Constantly returns multiple readings as defined using MultipleSetup&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 7&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Bytes as defined by MultipleSetup&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This call is similar to MultipleRead but continues to&lt;br /&gt;
		return readings until SmartBox receives the byte 123&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 8 IdentSystem ==&lt;br /&gt;
Read System Information&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = table length&lt;br /&gt;
* Word = VIA&lt;br /&gt;
* Word = ACIA&lt;br /&gt;
* Word = ADC&lt;br /&gt;
* Word = AUX.PORT&lt;br /&gt;
* Word = jobs.status&lt;br /&gt;
* Word = jobin.buf&lt;br /&gt;
* Word = jobout.buf&lt;br /&gt;
* Byte = Processor ident&lt;br /&gt;
* Word = OS version number&lt;br /&gt;
* Byte = Hardware version number&lt;br /&gt;
* String10 = Name, padded to 10 chars&lt;br /&gt;
* Byte = Number of inputs&lt;br /&gt;
* Byte = Number of outputs&lt;br /&gt;
* Byte = Number of motors&lt;br /&gt;
* Byte = Number of analogues&lt;br /&gt;
* Byte = BBR support&lt;br /&gt;
* Byte = Short support&lt;br /&gt;
* Byte = Printer support&lt;br /&gt;
* Byte = Keypad support&lt;br /&gt;
* Byte = LCD x dim/support&lt;br /&gt;
* Byte = LCD y dim&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3,4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 9 Credits ==&lt;br /&gt;
&lt;br /&gt;
Returns the copyright string&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 9&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* StringNUL = Copyright string&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This call returns the copyright message and OS details&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 10 WriteMotors ==&lt;br /&gt;
&lt;br /&gt;
Writes a byte to the motor drivers&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 10&lt;br /&gt;
* Byte 1 = value to write&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
Each of the four motor outputs has two bits encoded into this value, bits 0 and 1 are for motor a, bytes 2 and 3 are for motor b, bits 4 and 5 are for motor c and bits 6 and 7 are for motor d, setting both bits to 0 will stop the motor, setting the low bit high and high bit low will make the motor go forward and setting the high bit high and the low bit low will make it go backwards. Using this call automatically stops all pulsing of motors&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 11 ReadMotors ==&lt;br /&gt;
&lt;br /&gt;
Read the state of the motor drivers&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 11&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value read from the motor drivers&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
The value returned is the same value as what would be sent to WriteMotors&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 12 MotorForward ==&lt;br /&gt;
&lt;br /&gt;
Switch a motor on&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 12&lt;br /&gt;
* Byte 1 = motor number (1 to 4)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This makes the appropriate motor go forward. Using this call automatically cancels the pulsing for the appropriate motor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 13 MotorReverse ==&lt;br /&gt;
&lt;br /&gt;
Switch a motor on with reverse polarity&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 13&lt;br /&gt;
* Byte 1 = motor number (1 to 4)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This makes the appropriate motor go backward. Using this call automatically cancels the pulsing for the appropriate motor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 14 MotorHalt ==&lt;br /&gt;
&lt;br /&gt;
Switch a motor off&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 14&lt;br /&gt;
* Byte 1 = motor number (1 to 4)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This makes the appropriate motor stop. Using this call automatically cancels the pulsing for the appropriate motor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 15 MotorPower ==&lt;br /&gt;
&lt;br /&gt;
Pulse a motor outputs to vary speed&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 15&lt;br /&gt;
* Byte 1 = motor number (1 to 4)&lt;br /&gt;
* Byte 2 = on time&lt;br /&gt;
* Byte 3 = off time&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This pulses the appropriate motor at a rate determined by the on/off time (in centiseconds) specified, alternating the motor between the state at which it was last defined (Forward or Reverse) and Halt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 16 PatchMF ==&lt;br /&gt;
&lt;br /&gt;
Same as MotorForward&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|OS Release||&lt;br /&gt;
3,4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 17 MotorVoltage ==&lt;br /&gt;
&lt;br /&gt;
Set voltage for motor outputs&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = voltage (0 to 3)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
Voltage values are 0 (0v), 1 (6v), 2 (9v), 3 (12v).&amp;lt;br /&amp;gt;&lt;br /&gt;
''Setting a voltage of 0 will also switch '''outputs''' to 0v''&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This sets the actual voltage supplied to the motor outputs&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 20 WriteOutputs ==&lt;br /&gt;
&lt;br /&gt;
Write a 8 bit value to the digital output port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 20&lt;br /&gt;
* Byte 1 = byte to be written to the port&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
Using this call automatically cancels all pulsing on the output port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 21 OutputPower ==&lt;br /&gt;
&lt;br /&gt;
Vary the power by pulsing individual output lines&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 21&lt;br /&gt;
* Byte 1 = bit number (0 to 7)&lt;br /&gt;
* Byte 2 = on time&lt;br /&gt;
* Byte 3 = off time&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This pulses the appropriate output line at a rate determined by the on/off time (in centiseconds) specified, alternating the output line	between on and off&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 22 GetSensors ==&lt;br /&gt;
&lt;br /&gt;
To read the type of sensors connected to the analogue sensors&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 22&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 1 = type of sensor connected to sensor A&lt;br /&gt;
* Byte 2 = type of sensor connected to sensor B&lt;br /&gt;
* Byte 3 = type of sensor connected to sensor C&lt;br /&gt;
* Byte 4 = type of sensor connected to sensor D&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This returns the sensor types connected to the analogue sensors	that were returned from the last time they were checked. A sensor type of 0 means no sensor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 23 CheckSensors ==&lt;br /&gt;
&lt;br /&gt;
To check the type of sensors connected to the analogue sensors&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 23&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 1 = type of sensor connected to sensor A&lt;br /&gt;
* Byte 2 = type of sensor connected to sensor B&lt;br /&gt;
* Byte 3 = type of sensor connected to sensor C&lt;br /&gt;
* Byte 4 = type of sensor connected to sensor D&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This does an immediate check of the sensor types connected to the analogue sensors. A sensor type of 0 means no sensor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 24 WriteSensorTable ==&lt;br /&gt;
&lt;br /&gt;
Write sensor table entry&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Notes||&lt;br /&gt;
''Not Implemented''&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 25 ReadSensorTable ==&lt;br /&gt;
&lt;br /&gt;
Read sensor table entry&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = entry to read&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = sensor number&lt;br /&gt;
* Byte 1 = number of decimal points to display&lt;br /&gt;
* Byte 2 = max adc reading&lt;br /&gt;
* Byte 3 = reading offset (LSB)&lt;br /&gt;
* Byte 4 = reading offset (MSB)&lt;br /&gt;
* Byte 5 = multiplication factor (LSB)&lt;br /&gt;
* Byte 6 = multiplication factor (MSB)&lt;br /&gt;
* Byte 7 = division factor (LSB)&lt;br /&gt;
* Byte 8 = division factor (LSB)&lt;br /&gt;
* String = full sensor title&lt;br /&gt;
* String = abbreviated sensor label&lt;br /&gt;
* String = sensor label&lt;br /&gt;
* String = sensor units&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
If byte 0 is &amp;amp;FF then sensor is unknown&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3,4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 28 SetBitHigh ==&lt;br /&gt;
&lt;br /&gt;
Set individual output line(s) high&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 28&lt;br /&gt;
* Byte 1 = byte determining which lines will be set&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
A bit set in the byte sent will set the corresponding output line high&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 29 SetBitLow ==&lt;br /&gt;
&lt;br /&gt;
Set individual output line(s) low&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 29&lt;br /&gt;
* Byte 1 = byte determining which lines will be unset&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
A bit set in the byte sent will set the corresponding output line low&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 30 ReadADCReg ==&lt;br /&gt;
&lt;br /&gt;
To read a ADC register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 30&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of register&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 31 WriteADCReg ==&lt;br /&gt;
&lt;br /&gt;
To write to a ADC register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 31&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
* Byte 2 = value to write&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 32 ReadACIAReg ==&lt;br /&gt;
&lt;br /&gt;
To read a ACIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 32&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of register&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 33 WriteACIAReg ==&lt;br /&gt;
&lt;br /&gt;
To write to a ACIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 33&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
* Byte 2 = value to write&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 34 ReadVIAReg ==&lt;br /&gt;
&lt;br /&gt;
To read a VIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 34&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of register&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 35 WriteVIAReg ==&lt;br /&gt;
&lt;br /&gt;
To write to a VIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 35&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
* Byte 2 = value to write&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 36 SetVIAHigh ==&lt;br /&gt;
&lt;br /&gt;
To set bits in a VIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 36&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
* Byte 2 = mask&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 37 SetVIALow ==&lt;br /&gt;
&lt;br /&gt;
To unset bits in a VIA register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 37&lt;br /&gt;
* Byte 1 = register number (0 to 15)&lt;br /&gt;
* Byte 2 = mask&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 40 ReadADC ==&lt;br /&gt;
&lt;br /&gt;
Take a reading from a specific ADC channel&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 40&lt;br /&gt;
* Byte 1 = channel number (1 to 4)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* For an 8 bit reading:&lt;br /&gt;
** Byte 0 = reading from ADC&lt;br /&gt;
* For a 16 bit reading:&lt;br /&gt;
** Byte 0 = low byte of reading&lt;br /&gt;
** Byte 1 = high byte of reading&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
The value returned will be at the resolution specified by OS calls HighResADC and LowResADC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 41 ReadADCs ==&lt;br /&gt;
&lt;br /&gt;
Read all the ADC channels&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 41&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* For 8 bit readings:&lt;br /&gt;
** Byte 0 = reading from channel 1&lt;br /&gt;
** Byte 1 = reading from channel 2&lt;br /&gt;
** Byte 2 = reading from channel 3&lt;br /&gt;
** Byte 3 = reading from channel 4&lt;br /&gt;
* For 16 bit readings&lt;br /&gt;
** Byte 0 = reading from channel 1 (low byte)&lt;br /&gt;
** Byte 1 = reading from channel 1 (high byte)&lt;br /&gt;
** Byte 2 = reading from channel 2 (low byte)&lt;br /&gt;
** Byte 3 = reading from channel 2 (high byte)&lt;br /&gt;
** Byte 4 = reading from channel 3 (low byte)&lt;br /&gt;
** Byte 5 = reading from channel 3 (high byte)&lt;br /&gt;
** Byte 6 = reading from channel 4 (low byte)&lt;br /&gt;
** Byte 7 = reading from channel 4 (high byte)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 42 ForcedADCRead ==&lt;br /&gt;
&lt;br /&gt;
Force the A to D convertor to make a conversion and return the result&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 42&lt;br /&gt;
* Byte 1 = channel number (1 to 4)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* For an 8 bit reading:&lt;br /&gt;
** Byte 0 = reading from ADC&lt;br /&gt;
* For a 16 bit reading&lt;br /&gt;
** Byte 0 = reading from ADC (low byte)&lt;br /&gt;
** Byte 1 = reading from ADC (high byte)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 44 HighResADC ==&lt;br /&gt;
&lt;br /&gt;
Sets the resolution of ADC readings to 16 bit&lt;br /&gt;
Subsequent readings will return 2 byte values&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 44&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
For OS 2 readings returned will be only accurate to 10 bits. OS 3,4 provides support for 16 bit readings, but only to an accuracy of 8 bits.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 45 LowResADC ==&lt;br /&gt;
&lt;br /&gt;
Sets the resolution of ADC readings to 8 bit&lt;br /&gt;
Subsequent readings will return single byte values&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 45&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 47 ReadResolution ==&lt;br /&gt;
&lt;br /&gt;
Reads the current ADC resolution setting&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 47&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = resolution setting, where:&lt;br /&gt;
*: 0 = 8 bit&lt;br /&gt;
*: 1 = 16 bit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 50 DownloadData (OS 2), DownloadData740 (OS 3), DownloadData375 (OS 4) ==&lt;br /&gt;
&lt;br /&gt;
Download data into the SmartBox's memory&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 50&lt;br /&gt;
* Byte 1 = start address to write (low byte)&lt;br /&gt;
* Byte 2 = start address to write (high byte)&lt;br /&gt;
* Byte 3 = length of data (low byte)&lt;br /&gt;
* Byte 4 = length of data (high byte)&lt;br /&gt;
* Bytes 5 - n = data&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 52 UploadData (OS 2), UploadData740 (OS 3), UploadData (OS 4) ==&lt;br /&gt;
&lt;br /&gt;
Upload data from the SmartBox's memory&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 52&lt;br /&gt;
* Byte 1 = start address to read (low byte)&lt;br /&gt;
* Byte 2 = start address to read (high byte)&lt;br /&gt;
* Byte 3 = length of data (low byte)&lt;br /&gt;
* Byte 4 = length of data (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 - n = data&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 54 ExecuteCode (OS 2), ExecuteCode740 (OS 3), ExecuteCode375 (OS 4) ==&lt;br /&gt;
&lt;br /&gt;
Execute machine code held at a specified address&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 54&lt;br /&gt;
* Byte 1 = execution address (low byte)&lt;br /&gt;
* Byte 2 = execution address (high byte)&lt;br /&gt;
* Byte 3 = contents of the A register on entry to the code&lt;br /&gt;
* Byte 4 = contents of the X register on entry to the code&lt;br /&gt;
* Byte 5 = contents of the Y register on entry to the code&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 55 StoreByte (OS 2), StoreByte740 (OS 3), StoreByte375 (OS 4) ==&lt;br /&gt;
&lt;br /&gt;
Store a byte in the SmartBox's RAM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 55&lt;br /&gt;
* Byte 1 = address to write (low byte)&lt;br /&gt;
* Byte 2 = address to write (high byte)&lt;br /&gt;
* Byte 3 = byte to be stored&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 56 ReadByte (OS 2), ReadByte740 (OS 3), ReadByte (OS 4) ==&lt;br /&gt;
&lt;br /&gt;
Read a byte from the SmartBox's RAM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 56&lt;br /&gt;
* Byte 1 = address to read (low byte)&lt;br /&gt;
* Byte 2 = address to read (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = byte read&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 57 ReadRAMSize ==&lt;br /&gt;
&lt;br /&gt;
Read the amount of RAM with which the SmartBox is fitted&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 57&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = RAM size (low byte)&lt;br /&gt;
* Byte 1 = RAM size (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 59 ExtendCall ==&lt;br /&gt;
&lt;br /&gt;
Call the extended call vector&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 59&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = extension value&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
This call provides the user with the possibility of adding extra calls to SmartBox easily&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 60 SetClock ==&lt;br /&gt;
&lt;br /&gt;
Set the internal clock in SmartBox. This clock only runs while the power in maintained&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 60&lt;br /&gt;
* Byte 1 = 1/10 seconds (0 to 9)&lt;br /&gt;
* Byte 2 = seconds (0 to 59)&lt;br /&gt;
* Byte 3 = minutes (0 to 59)&lt;br /&gt;
* Byte 4 = hours (0 to 23)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
The day value is set to 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 61 ReadClock ==&lt;br /&gt;
&lt;br /&gt;
Read the internal clock. On reset the clock will be set to zero&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 61&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = 1/10 seconds&lt;br /&gt;
* Byte 1 = seconds&lt;br /&gt;
* Byte 2 = minutes&lt;br /&gt;
* Byte 3 = hours&lt;br /&gt;
* Byte 4 = days&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 62 ReadTopmem ==&lt;br /&gt;
&lt;br /&gt;
Read the current value of TOPMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 62&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of TOPMEM (low byte)&lt;br /&gt;
* Byte 1 = value of TOPMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 63 WriteTopmem ==&lt;br /&gt;
&lt;br /&gt;
Write the value of TOPMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 63&lt;br /&gt;
* Byte 1 = value of TOPMEM (low byte)&lt;br /&gt;
* Byte 2 = value of TOPMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 64 ReadLomem ==&lt;br /&gt;
&lt;br /&gt;
Read the current value of LOMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 64&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of LOMEM (low byte)&lt;br /&gt;
* Byte 1 = value of LOMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 65 WriteLomem ==&lt;br /&gt;
&lt;br /&gt;
Write the value of LOMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 65&lt;br /&gt;
* Byte 1 = value of LOMEM (low byte)&lt;br /&gt;
* Byte 2 = value of LOMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 66 ReadHimem ==&lt;br /&gt;
&lt;br /&gt;
Read the current value of HIMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 66&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of HIMEM (low byte)&lt;br /&gt;
* Byte 1 = value of HIMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 67 WriteHimem ==&lt;br /&gt;
&lt;br /&gt;
Write the value of HIMEM&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 67&lt;br /&gt;
* Byte 1 = value of HIMEM (low byte)&lt;br /&gt;
* Byte 2 = value of HIMEM (high byte)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
2,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 70 WritePrinter ==&lt;br /&gt;
&lt;br /&gt;
Write printer port (no handshaking)&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = byte to write&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 71 ReadPrinter == &lt;br /&gt;
&lt;br /&gt;
Read printer port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = printer port value&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 72 PrintChar ==&lt;br /&gt;
&lt;br /&gt;
Send a character to printer port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = char&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 73 PrintStreamZ ==&lt;br /&gt;
&lt;br /&gt;
Send a stream of characters to the printer port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* StringNUL = string to print&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 74 PrintStream ==&lt;br /&gt;
&lt;br /&gt;
Send a stream of characters to the printer port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = number of bytes to send (LSB)&lt;br /&gt;
* Byte 1 = number of bytes to send (MSB)&lt;br /&gt;
* Bytes = stream of characters to print&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 75 PrintServer ==&lt;br /&gt;
&lt;br /&gt;
Echo all characters to printer port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Notes||&lt;br /&gt;
No exit		&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 80 WriteRTCReg ==&lt;br /&gt;
&lt;br /&gt;
Write a RTC register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = register&lt;br /&gt;
* Byte 1 = value&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 81 ReadRTCReg ==&lt;br /&gt;
&lt;br /&gt;
Read a RTC register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = register&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 82 WriteRTC ==&lt;br /&gt;
&lt;br /&gt;
Writes RTC as a string&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* StringCR = RTC time&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
''Not currently implemented''&amp;lt;br /&amp;gt;&lt;br /&gt;
Time is represented as DDD,dd mmm yyyy.hh:mm:ss&lt;br /&gt;
* DDD is 3 character day (Mon, Tue, Wed, Thu, Fri, Sat, Sun)&lt;br /&gt;
* dd is day of month&lt;br /&gt;
* mmm is month&lt;br /&gt;
* yyyy is year&lt;br /&gt;
* hh is hours&lt;br /&gt;
* mm is minutes&lt;br /&gt;
* ss is seconds&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 83 ReadRTC ==&lt;br /&gt;
&lt;br /&gt;
Reads RTC as a string&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* StringCR = RTC time&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
Time is represented as DDD,dd mmm yyyy.hh:mm:ss&lt;br /&gt;
* DDD is 3 character day (Mon, Tue, Wed, Thu, Fri, Sat, Sun)&lt;br /&gt;
* dd is day of month&lt;br /&gt;
* mmm is month&lt;br /&gt;
* yyyy is year&lt;br /&gt;
* hh is hours&lt;br /&gt;
* mm is minutes&lt;br /&gt;
* ss is seconds&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 84 WriteRTCbcd ==&lt;br /&gt;
&lt;br /&gt;
Writes all 7 RTC time registers&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = seconds&lt;br /&gt;
* Byte 1 = minutes&lt;br /&gt;
* Byte 2 = hours&lt;br /&gt;
* Byte 3 = date&lt;br /&gt;
* Byte 4 = month&lt;br /&gt;
* Byte 5 = day&lt;br /&gt;
* Byte 6 = year&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
All values are represented in BCD&lt;br /&gt;
* Years below 80 (&amp;amp;80 in BCD) signify 20xx&lt;br /&gt;
* Day starts (0) from Monday&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 85 ReadRTCbcd ==&lt;br /&gt;
&lt;br /&gt;
Reads all 7 RTC time registers&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = seconds&lt;br /&gt;
* Byte 1 = minutes&lt;br /&gt;
* Byte 2 = hours&lt;br /&gt;
* Byte 3 = date&lt;br /&gt;
* Byte 4 = month&lt;br /&gt;
* Byte 5 = day&lt;br /&gt;
* Byte 6 = year&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
All values are represented in BCD&lt;br /&gt;
* Years below 80 (&amp;amp;80 in BCD) signify 20xx&lt;br /&gt;
* Day starts (0) from Monday&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 90 ReadInputs ==&lt;br /&gt;
&lt;br /&gt;
Reads a byte from the digital inputs port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 90&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = byte read from the digital inputs port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 91 ReadBit ==&lt;br /&gt;
&lt;br /&gt;
Reads a bit from the digital inputs port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 91&lt;br /&gt;
* Byte 1 = byte to read (0 to 7)&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = bit read from the digital inputs port&lt;br /&gt;
|-&lt;br /&gt;
|Commments||&lt;br /&gt;
Reads an individual sensor from the digital inputs port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 92 ReadOutputs ==&lt;br /&gt;
&lt;br /&gt;
Reads a byte from the digital outputs port&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 92&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = byte read from the digital outputs port&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
This reads the soft copy of the last written byte&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3,4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 93 CountReset ==&lt;br /&gt;
&lt;br /&gt;
Resets input port counter(s)&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 93&lt;br /&gt;
* Byte 1 = bit masks for counters to reset&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 94 CountRead ==&lt;br /&gt;
&lt;br /&gt;
Reads input port counter(s)&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = 94&lt;br /&gt;
* Byte 1 = counter to read (0 to 7) or 255 for all&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = value of counter (LSB)&lt;br /&gt;
* Byte 1 = value of counter (MSB)&lt;br /&gt;
* Byte n = value of counter (LSB)&lt;br /&gt;
* Byte n+1 = value of counter (MSB)&lt;br /&gt;
|-&lt;br /&gt;
|Notes||&lt;br /&gt;
Counter values return 65535 (-1) from box reset, or max count&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 98 InsightDriver ==&lt;br /&gt;
&lt;br /&gt;
Starts the internal Insight software&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 99 DataApp ==&lt;br /&gt;
&lt;br /&gt;
Starts the internal DataApp software&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 100 WriteLCDReg ==&lt;br /&gt;
&lt;br /&gt;
Write LCD Register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = register number&lt;br /&gt;
* Byte 1 = register value&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 101 ReadLCDReg ==&lt;br /&gt;
&lt;br /&gt;
Read LCD Register&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = register number&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = register value&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 102 LCDChar ==&lt;br /&gt;
&lt;br /&gt;
Send a character to the LCD display driver&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = char&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 103 LCDStreamZ ==&lt;br /&gt;
&lt;br /&gt;
Send a stream of characters to the LCD display driver&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* StringNUL = string to display&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 104 LCDStream ==&lt;br /&gt;
&lt;br /&gt;
Send a stream of characters to the LCD display driver&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
* Byte 0 = number of bytes to send (LSB)&lt;br /&gt;
* Byte 1 = number of bytes to send (MSB)&lt;br /&gt;
* Bytes = stream of characters to display&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 110 ReadKeypad ==&lt;br /&gt;
&lt;br /&gt;
Reads the raw state of the keypad&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = current keypad reading bitstate&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 111 ReadKeypadPress ==&lt;br /&gt;
&lt;br /&gt;
Reads the processed state of the keypad&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Send||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Returns||&lt;br /&gt;
* Byte 0 = current keypad press bitstate&lt;br /&gt;
|-&lt;br /&gt;
|OS Release||&lt;br /&gt;
3&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=276</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=276"/>
		<updated>2023-11-04T17:44:44Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Changelog */ Put changelog in table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = JobCode&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf should contain any input data&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = flag&lt;br /&gt;
* X = number of bytes given back by routine&lt;br /&gt;
* Y = undefined&lt;br /&gt;
* c = status, if set JobCall does not exist&lt;br /&gt;
* z = status, if set JobCall cannot be called internally&lt;br /&gt;
* jobout_buf contains any data sent from routine&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_PRINTER: Place a character in the printer buffer ===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* if c = 0 (printed)&lt;br /&gt;
*: The printer is on and the character was inserted&lt;br /&gt;
* if c = 1 (not printed)&lt;br /&gt;
*: The printer is off and the character was forgotten&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset!!Value!!Comment&lt;br /&gt;
|-&lt;br /&gt;
|0||&amp;quot;Module&amp;quot;||The OS checks for this string&lt;br /&gt;
|-&lt;br /&gt;
|6||0||End of check string&lt;br /&gt;
|-&lt;br /&gt;
|7||Language entry||Address of 'Language' entry&lt;br /&gt;
|-&lt;br /&gt;
|9||Service entry||Address of 'Service' entry&lt;br /&gt;
|-&lt;br /&gt;
|11||&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot;||Module title&lt;br /&gt;
|-&lt;br /&gt;
| ||0||End of Module title&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;||Part Module Title&lt;br /&gt;
|-&lt;br /&gt;
| ||0||End of Part Module Title&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;quot;x.xx&amp;quot;||Part version number&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;amp;FF||End of parts list&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropriate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
* 0	- Not used&lt;br /&gt;
* 1	- Unknown Job Code&lt;br /&gt;
* 2	- Centisecond call&lt;br /&gt;
* 3	- irq2 (unknown IRQ)&lt;br /&gt;
* 4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
* 254	- RESET&lt;br /&gt;
* 255	- BRK&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the environment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
== Example JobCall ==&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;DIM data% &amp;amp;1000&lt;br /&gt;
:&lt;br /&gt;
no_of_calls = 1&lt;br /&gt;
:&lt;br /&gt;
VIA = &amp;amp;E030&lt;br /&gt;
ACIA = &amp;amp;E010&lt;br /&gt;
ADC = &amp;amp;E000&lt;br /&gt;
AUX_PORT = &amp;amp;E020&lt;br /&gt;
brk_vec = &amp;amp;200&lt;br /&gt;
nmi_vec = &amp;amp;202&lt;br /&gt;
irq_vec = &amp;amp;204&lt;br /&gt;
irq2_vec = &amp;amp;206&lt;br /&gt;
sendserial_vec = &amp;amp;208&lt;br /&gt;
readserial_vec = &amp;amp;20A&lt;br /&gt;
sendjob_vec = &amp;amp;20C&lt;br /&gt;
readjob_vec = &amp;amp;20E&lt;br /&gt;
decodejob_vec = &amp;amp;210&lt;br /&gt;
unknownjob_vec = &amp;amp;212&lt;br /&gt;
extjob_vec = &amp;amp;214&lt;br /&gt;
centisec_vec = &amp;amp;216&lt;br /&gt;
internal_vec = &amp;amp;218&lt;br /&gt;
callos_vec = &amp;amp;21A&lt;br /&gt;
printer_vec = &amp;amp;21C&lt;br /&gt;
zero_gp1 = 0&lt;br /&gt;
zero_gp2 = 2&lt;br /&gt;
zero_gp3 = 4&lt;br /&gt;
zero_gp4 = 6&lt;br /&gt;
zero_gp5 = 8&lt;br /&gt;
zero_gp6 = 10&lt;br /&gt;
zero_gp7 = 12&lt;br /&gt;
zero_gp8 = 14&lt;br /&gt;
zero_gp9 = 16&lt;br /&gt;
zero_gp10 = 18&lt;br /&gt;
user_reserved = &amp;amp;70&lt;br /&gt;
irq_A = &amp;amp;A0&lt;br /&gt;
fcount = &amp;amp;A1&lt;br /&gt;
RAM_size = &amp;amp;A3&lt;br /&gt;
jobout_buf = &amp;amp;400&lt;br /&gt;
jobin_buf = &amp;amp;480&lt;br /&gt;
OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
OS_CALLOS = &amp;amp;FFBC&lt;br /&gt;
OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
:&lt;br /&gt;
FOR create=1 TO 2&lt;br /&gt;
  :&lt;br /&gt;
  FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
    P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
    [OPT pass%&lt;br /&gt;
    :&lt;br /&gt;
    .job_DemoJob&lt;br /&gt;
    CMP #1&lt;br /&gt;
    BEQ DemoJob_go&lt;br /&gt;
    LDA #1&lt;br /&gt;
    LDY #0&lt;br /&gt;
    RTS&lt;br /&gt;
    /&lt;br /&gt;
    .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    :&lt;br /&gt;
    .internal_handle&lt;br /&gt;
    CMP #1; Is it function call 1 (NameCode) ?&lt;br /&gt;
    BNE internal_handle_2; No, check for function 2&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID number&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #2; CALLOS function 2&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #1; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_2&lt;br /&gt;
    CMP #2; Is it function call 2 (CodeName) ?&lt;br /&gt;
    BNE internal_handle_3; No, unknown, pass on&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #3; CALLOS function 3&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #2; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_yes&lt;br /&gt;
    LDA #0; We want to claim call for some reason&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_3&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    :&lt;br /&gt;
    .job_handle&lt;br /&gt;
    PHA; Make sure to preserve A&lt;br /&gt;
    CPY call; Check call wanted against base of ours&lt;br /&gt;
    BCC job_handle_no; Less than our base, so definately not ours&lt;br /&gt;
    TYA&lt;br /&gt;
    SBC #no_of_calls; Subtract number of calls we have&lt;br /&gt;
    CMP call; Now compare with base&lt;br /&gt;
    BCC job_handle2; Yes, one of ours&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle.no&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (ovec2); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle2&lt;br /&gt;
    TYA&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC call; Subtract our base from call&lt;br /&gt;
    ASL A; Times by 2 for offset into table&lt;br /&gt;
    TAY; Place in Y&lt;br /&gt;
    LDA job_run_table,Y; Get LSB of routine&lt;br /&gt;
    STA zero_gp1; Store it for indirect jump (LSB)&lt;br /&gt;
    LDA job_run_table+1,Y; Get MSB of routine&lt;br /&gt;
    STA zero_gp1+1; Store it for indirect jump (MSB)&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (zero_gp1); Call our relevant routine&lt;br /&gt;
    :&lt;br /&gt;
    .job_names; List of our JobNames&lt;br /&gt;
    EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13; Test job name&lt;br /&gt;
    /&lt;br /&gt;
    EQUB &amp;amp;FF; &amp;amp;FF - marks end of table&lt;br /&gt;
    :&lt;br /&gt;
    .job_run_table; List of routine addresses to match Jobs&lt;br /&gt;
    EQUW job_DemoJob; Test job routine&lt;br /&gt;
    :&lt;br /&gt;
    .call   : EQUB 0; Store for our JobCode base&lt;br /&gt;
    .job_id : EQUB 0; Store for our JobId&lt;br /&gt;
    .ovec   : EQUW 0; Store for old internal_vec value&lt;br /&gt;
    .ovec2  : EQUW 0; Store for old unknownjob_vec value&lt;br /&gt;
    :&lt;br /&gt;
    EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
    .end_of_code; end_of_code should be on page boundry&lt;br /&gt;
    :&lt;br /&gt;
    .execute&lt;br /&gt;
    LDX #no_of_calls; Number of JobCodes wanted&lt;br /&gt;
    LDA #1; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    STX call; Store base address of JobCodes allocated&lt;br /&gt;
    STY job_id; Store JOB ID given&lt;br /&gt;
    CPX #0; Check we were given some codes&lt;br /&gt;
    BEQ noinstall; No, don't bother installing ourself&lt;br /&gt;
    /&lt;br /&gt;
    LDA internal_vec; Get old internal_vec (LSB)&lt;br /&gt;
    STA ovec; Store it&lt;br /&gt;
    LDA internal_vec+1; Get old internal_vec (MSB)&lt;br /&gt;
    STA ovec+1; Store it&lt;br /&gt;
    LDA #internal_handle MOD 256; Our internal handler (LSB)&lt;br /&gt;
    STA internal_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #internal_handle DIV 256; Our internal handler (MSB)&lt;br /&gt;
    STA internal_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDA unknownjob_vec; Get old unknownjob_vec (LSB)&lt;br /&gt;
    STA ovec2; Store it&lt;br /&gt;
    LDA unknownjob_vec+1; Get old unknownjob_vec (MSB)&lt;br /&gt;
    STA ovec2+1; Store it&lt;br /&gt;
    LDA #job_handle MOD 256; Our unknown job handler (LSB)&lt;br /&gt;
    STA unknownjob_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #job_handle DIV 256; Our unknown job handler (MSB)&lt;br /&gt;
    STA unknownjob_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDX #end_of_code MOD 256; End of code (LSB)&lt;br /&gt;
    LDY #end_of_code DIV 256; End of code (MSB)&lt;br /&gt;
    STX jobin_buf; Place it in jobin_buf (LSB)&lt;br /&gt;
    STY jobin_buf+1; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
    LDA #65; JobCode for WriteLomem&lt;br /&gt;
    JSR OS_DECODEJOB; Call JobCall handler&lt;br /&gt;
    /&lt;br /&gt;
    .noinstall&lt;br /&gt;
    RTS; End - return to OS&lt;br /&gt;
    :&lt;br /&gt;
    .execcall&lt;br /&gt;
    LDA #&amp;gt;(execute-address); Final execution address offset (LSB)&lt;br /&gt;
    STA zero_gp1&lt;br /&gt;
    LDA #&amp;lt;(execute-address); Final execution address offset (MSB)&lt;br /&gt;
    STA zero_gp1+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(BitMap-address); BitMap address offset (LSB)&lt;br /&gt;
    STA zero_gp2&lt;br /&gt;
    LDA #&amp;lt;(BitMap-address); BitMap address offset (MSB)&lt;br /&gt;
    STA zero_gp2+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
    STA zero_gp3&lt;br /&gt;
    LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
    STA zero_gp3+1&lt;br /&gt;
    /&lt;br /&gt;
    TXA; Execcall REAL address&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC #&amp;gt;(execcall-address); Subtract length&lt;br /&gt;
    TAX; Put it back in X&lt;br /&gt;
    TYA; Ditto for MSB&lt;br /&gt;
    SBC #&amp;lt;(execcall-address)&lt;br /&gt;
    TAY&lt;br /&gt;
    LDA #4; CALLOS function for Relocate&lt;br /&gt;
    JMP OS_CALLOS; Call CALLOS&lt;br /&gt;
    /&lt;br /&gt;
    .BitMap; BitMap start&lt;br /&gt;
    :&lt;br /&gt;
    ]&lt;br /&gt;
  NEXT&lt;br /&gt;
  :&lt;br /&gt;
  OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
  :&lt;br /&gt;
NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|bob||071||Fixed MotorForward bug (%10)&lt;br /&gt;
|-&lt;br /&gt;
|bob||072||CallOS 14 now checks current adc.owner status&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||* Rewrite for new hardware/processor *&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Moved jobout.buf to &amp;amp;600&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Moved jobin.buf to &amp;amp;680&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Moved jobs.status to &amp;amp;700&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added irq.X (&amp;amp;A4)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added irq.Y (&amp;amp;A5)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Removed short detection code&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 21 Write Outputs&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 22 Read Outputs added&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 23 Read Inputs added&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 24 Write Motors&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 25 Read Motors&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added CallOS 26 Read Keypad&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Expanded CallOS 5 information&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||JobCalls now use appropriate CallOS'&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||OS.READBYTE no longer preserves A on CS&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Renamed JobCall UploadData to UploadData38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d1||JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 27 Write printer&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 28 Read printer&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 29 Write RTC Reg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 30 Read RTC Reg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 31 Write RTC string&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 32 Read RTC string&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 33 Write RTC bcd&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 34 Read RTC bcd&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 35 Write LCD Reg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 36 Read LCD Reg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall WritePrinter&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall ReadPrinter&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall PrintChar&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall PrintStreamZ&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall PrintStream&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall PrintServer&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall WriteRTCReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall ReadRTCReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall WriteRTC&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall ReadRTC&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall WriteRTCbcd&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall ReadRTCbcd&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall WriteLCDReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall ReadLCDReg&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 37 Write Power ctrl&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added CallOS 38 Read Power ctrl&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Implemented OS.PRINTER&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added JobCall PatchMF (MotorForward)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Changed irq.vec to int0.vec&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Changed irq2.vec to int1.vec&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d3||Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Hardware update&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d4||Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||OS.CALLOS now re-entrant&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||Fixed OS.PRINTER&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||Started to add internal logging software&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d5||Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.d7||Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
|-&lt;br /&gt;
|bob||073||Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
|-&lt;br /&gt;
|bob||073||Added PatchMF&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dj||Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dj||Added hard reset keypad press&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dk||Added JobCall ReadOutputs&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.do||Changed soft reset of zero page locations&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.do||Changed MotorForward/Backward bitmaps&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dp||Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dq||Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dr||Changed reset prompts&lt;br /&gt;
|-&lt;br /&gt;
|bob||074||Added support for Little Bob&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dy||Added further RTC/CMOS support&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dB||Removed double hard reset&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dE||Set _cpu.mode depending on external memory requirement&lt;br /&gt;
|-&lt;br /&gt;
|jim||072.dF||Added Insight code&lt;br /&gt;
|-&lt;br /&gt;
|jim||073||Sub-release&lt;br /&gt;
|-&lt;br /&gt;
|jim||074||Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
|-&lt;br /&gt;
|jim||074||Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
|-&lt;br /&gt;
|jim||074||Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Added simple battery charge code&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Fixed JobCall ReadSensorTable&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Added sleep code&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
|-&lt;br /&gt;
|jim||075||Modified battery charge code&lt;br /&gt;
|-&lt;br /&gt;
|jim||076||Modified JobCall ReadSensorTable&lt;br /&gt;
|-&lt;br /&gt;
|jim||076||Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
|-&lt;br /&gt;
|jim||077||Rewrote battery charging code&lt;br /&gt;
|-&lt;br /&gt;
|jim||077.2||Reduced stop charge threshold to 2&lt;br /&gt;
|-&lt;br /&gt;
|jim||077.3||Increased stop charge threshold to 3&lt;br /&gt;
|-&lt;br /&gt;
|jim||077.3||Added battery voltage averaging&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Stripped&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Moved ins to out port&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Added RTS&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Changed sensor ID system back to original, using p2.1&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
|-&lt;br /&gt;
|bill||077||Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
|-&lt;br /&gt;
|bill||078||CTS added&lt;br /&gt;
|-&lt;br /&gt;
|bill||079||Temperature fudge&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=275</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=275"/>
		<updated>2023-11-04T17:42:37Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Modules */ Tidied up layout of modules documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = JobCode&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf should contain any input data&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = flag&lt;br /&gt;
* X = number of bytes given back by routine&lt;br /&gt;
* Y = undefined&lt;br /&gt;
* c = status, if set JobCall does not exist&lt;br /&gt;
* z = status, if set JobCall cannot be called internally&lt;br /&gt;
* jobout_buf contains any data sent from routine&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_PRINTER: Place a character in the printer buffer ===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* if c = 0 (printed)&lt;br /&gt;
*: The printer is on and the character was inserted&lt;br /&gt;
* if c = 1 (not printed)&lt;br /&gt;
*: The printer is off and the character was forgotten&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset!!Value!!Comment&lt;br /&gt;
|-&lt;br /&gt;
|0||&amp;quot;Module&amp;quot;||The OS checks for this string&lt;br /&gt;
|-&lt;br /&gt;
|6||0||End of check string&lt;br /&gt;
|-&lt;br /&gt;
|7||Language entry||Address of 'Language' entry&lt;br /&gt;
|-&lt;br /&gt;
|9||Service entry||Address of 'Service' entry&lt;br /&gt;
|-&lt;br /&gt;
|11||&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot;||Module title&lt;br /&gt;
|-&lt;br /&gt;
| ||0||End of Module title&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;||Part Module Title&lt;br /&gt;
|-&lt;br /&gt;
| ||0||End of Part Module Title&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;quot;x.xx&amp;quot;||Part version number&lt;br /&gt;
|-&lt;br /&gt;
| ||&amp;amp;FF||End of parts list&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropriate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
* 0	- Not used&lt;br /&gt;
* 1	- Unknown Job Code&lt;br /&gt;
* 2	- Centisecond call&lt;br /&gt;
* 3	- irq2 (unknown IRQ)&lt;br /&gt;
* 4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
* 254	- RESET&lt;br /&gt;
* 255	- BRK&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the environment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
== Example JobCall ==&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;DIM data% &amp;amp;1000&lt;br /&gt;
:&lt;br /&gt;
no_of_calls = 1&lt;br /&gt;
:&lt;br /&gt;
VIA = &amp;amp;E030&lt;br /&gt;
ACIA = &amp;amp;E010&lt;br /&gt;
ADC = &amp;amp;E000&lt;br /&gt;
AUX_PORT = &amp;amp;E020&lt;br /&gt;
brk_vec = &amp;amp;200&lt;br /&gt;
nmi_vec = &amp;amp;202&lt;br /&gt;
irq_vec = &amp;amp;204&lt;br /&gt;
irq2_vec = &amp;amp;206&lt;br /&gt;
sendserial_vec = &amp;amp;208&lt;br /&gt;
readserial_vec = &amp;amp;20A&lt;br /&gt;
sendjob_vec = &amp;amp;20C&lt;br /&gt;
readjob_vec = &amp;amp;20E&lt;br /&gt;
decodejob_vec = &amp;amp;210&lt;br /&gt;
unknownjob_vec = &amp;amp;212&lt;br /&gt;
extjob_vec = &amp;amp;214&lt;br /&gt;
centisec_vec = &amp;amp;216&lt;br /&gt;
internal_vec = &amp;amp;218&lt;br /&gt;
callos_vec = &amp;amp;21A&lt;br /&gt;
printer_vec = &amp;amp;21C&lt;br /&gt;
zero_gp1 = 0&lt;br /&gt;
zero_gp2 = 2&lt;br /&gt;
zero_gp3 = 4&lt;br /&gt;
zero_gp4 = 6&lt;br /&gt;
zero_gp5 = 8&lt;br /&gt;
zero_gp6 = 10&lt;br /&gt;
zero_gp7 = 12&lt;br /&gt;
zero_gp8 = 14&lt;br /&gt;
zero_gp9 = 16&lt;br /&gt;
zero_gp10 = 18&lt;br /&gt;
user_reserved = &amp;amp;70&lt;br /&gt;
irq_A = &amp;amp;A0&lt;br /&gt;
fcount = &amp;amp;A1&lt;br /&gt;
RAM_size = &amp;amp;A3&lt;br /&gt;
jobout_buf = &amp;amp;400&lt;br /&gt;
jobin_buf = &amp;amp;480&lt;br /&gt;
OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
OS_CALLOS = &amp;amp;FFBC&lt;br /&gt;
OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
:&lt;br /&gt;
FOR create=1 TO 2&lt;br /&gt;
  :&lt;br /&gt;
  FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
    P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
    [OPT pass%&lt;br /&gt;
    :&lt;br /&gt;
    .job_DemoJob&lt;br /&gt;
    CMP #1&lt;br /&gt;
    BEQ DemoJob_go&lt;br /&gt;
    LDA #1&lt;br /&gt;
    LDY #0&lt;br /&gt;
    RTS&lt;br /&gt;
    /&lt;br /&gt;
    .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    :&lt;br /&gt;
    .internal_handle&lt;br /&gt;
    CMP #1; Is it function call 1 (NameCode) ?&lt;br /&gt;
    BNE internal_handle_2; No, check for function 2&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID number&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #2; CALLOS function 2&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #1; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_2&lt;br /&gt;
    CMP #2; Is it function call 2 (CodeName) ?&lt;br /&gt;
    BNE internal_handle_3; No, unknown, pass on&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #3; CALLOS function 3&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #2; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_yes&lt;br /&gt;
    LDA #0; We want to claim call for some reason&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_3&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    :&lt;br /&gt;
    .job_handle&lt;br /&gt;
    PHA; Make sure to preserve A&lt;br /&gt;
    CPY call; Check call wanted against base of ours&lt;br /&gt;
    BCC job_handle_no; Less than our base, so definately not ours&lt;br /&gt;
    TYA&lt;br /&gt;
    SBC #no_of_calls; Subtract number of calls we have&lt;br /&gt;
    CMP call; Now compare with base&lt;br /&gt;
    BCC job_handle2; Yes, one of ours&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle.no&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (ovec2); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle2&lt;br /&gt;
    TYA&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC call; Subtract our base from call&lt;br /&gt;
    ASL A; Times by 2 for offset into table&lt;br /&gt;
    TAY; Place in Y&lt;br /&gt;
    LDA job_run_table,Y; Get LSB of routine&lt;br /&gt;
    STA zero_gp1; Store it for indirect jump (LSB)&lt;br /&gt;
    LDA job_run_table+1,Y; Get MSB of routine&lt;br /&gt;
    STA zero_gp1+1; Store it for indirect jump (MSB)&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (zero_gp1); Call our relevant routine&lt;br /&gt;
    :&lt;br /&gt;
    .job_names; List of our JobNames&lt;br /&gt;
    EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13; Test job name&lt;br /&gt;
    /&lt;br /&gt;
    EQUB &amp;amp;FF; &amp;amp;FF - marks end of table&lt;br /&gt;
    :&lt;br /&gt;
    .job_run_table; List of routine addresses to match Jobs&lt;br /&gt;
    EQUW job_DemoJob; Test job routine&lt;br /&gt;
    :&lt;br /&gt;
    .call   : EQUB 0; Store for our JobCode base&lt;br /&gt;
    .job_id : EQUB 0; Store for our JobId&lt;br /&gt;
    .ovec   : EQUW 0; Store for old internal_vec value&lt;br /&gt;
    .ovec2  : EQUW 0; Store for old unknownjob_vec value&lt;br /&gt;
    :&lt;br /&gt;
    EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
    .end_of_code; end_of_code should be on page boundry&lt;br /&gt;
    :&lt;br /&gt;
    .execute&lt;br /&gt;
    LDX #no_of_calls; Number of JobCodes wanted&lt;br /&gt;
    LDA #1; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    STX call; Store base address of JobCodes allocated&lt;br /&gt;
    STY job_id; Store JOB ID given&lt;br /&gt;
    CPX #0; Check we were given some codes&lt;br /&gt;
    BEQ noinstall; No, don't bother installing ourself&lt;br /&gt;
    /&lt;br /&gt;
    LDA internal_vec; Get old internal_vec (LSB)&lt;br /&gt;
    STA ovec; Store it&lt;br /&gt;
    LDA internal_vec+1; Get old internal_vec (MSB)&lt;br /&gt;
    STA ovec+1; Store it&lt;br /&gt;
    LDA #internal_handle MOD 256; Our internal handler (LSB)&lt;br /&gt;
    STA internal_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #internal_handle DIV 256; Our internal handler (MSB)&lt;br /&gt;
    STA internal_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDA unknownjob_vec; Get old unknownjob_vec (LSB)&lt;br /&gt;
    STA ovec2; Store it&lt;br /&gt;
    LDA unknownjob_vec+1; Get old unknownjob_vec (MSB)&lt;br /&gt;
    STA ovec2+1; Store it&lt;br /&gt;
    LDA #job_handle MOD 256; Our unknown job handler (LSB)&lt;br /&gt;
    STA unknownjob_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #job_handle DIV 256; Our unknown job handler (MSB)&lt;br /&gt;
    STA unknownjob_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDX #end_of_code MOD 256; End of code (LSB)&lt;br /&gt;
    LDY #end_of_code DIV 256; End of code (MSB)&lt;br /&gt;
    STX jobin_buf; Place it in jobin_buf (LSB)&lt;br /&gt;
    STY jobin_buf+1; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
    LDA #65; JobCode for WriteLomem&lt;br /&gt;
    JSR OS_DECODEJOB; Call JobCall handler&lt;br /&gt;
    /&lt;br /&gt;
    .noinstall&lt;br /&gt;
    RTS; End - return to OS&lt;br /&gt;
    :&lt;br /&gt;
    .execcall&lt;br /&gt;
    LDA #&amp;gt;(execute-address); Final execution address offset (LSB)&lt;br /&gt;
    STA zero_gp1&lt;br /&gt;
    LDA #&amp;lt;(execute-address); Final execution address offset (MSB)&lt;br /&gt;
    STA zero_gp1+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(BitMap-address); BitMap address offset (LSB)&lt;br /&gt;
    STA zero_gp2&lt;br /&gt;
    LDA #&amp;lt;(BitMap-address); BitMap address offset (MSB)&lt;br /&gt;
    STA zero_gp2+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
    STA zero_gp3&lt;br /&gt;
    LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
    STA zero_gp3+1&lt;br /&gt;
    /&lt;br /&gt;
    TXA; Execcall REAL address&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC #&amp;gt;(execcall-address); Subtract length&lt;br /&gt;
    TAX; Put it back in X&lt;br /&gt;
    TYA; Ditto for MSB&lt;br /&gt;
    SBC #&amp;lt;(execcall-address)&lt;br /&gt;
    TAY&lt;br /&gt;
    LDA #4; CALLOS function for Relocate&lt;br /&gt;
    JMP OS_CALLOS; Call CALLOS&lt;br /&gt;
    /&lt;br /&gt;
    .BitMap; BitMap start&lt;br /&gt;
    :&lt;br /&gt;
    ]&lt;br /&gt;
  NEXT&lt;br /&gt;
  :&lt;br /&gt;
  OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
  :&lt;br /&gt;
NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=274</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=274"/>
		<updated>2023-11-04T17:38:21Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Example JobCall */ Tidied up formatting/indentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = JobCode&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf should contain any input data&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = flag&lt;br /&gt;
* X = number of bytes given back by routine&lt;br /&gt;
* Y = undefined&lt;br /&gt;
* c = status, if set JobCall does not exist&lt;br /&gt;
* z = status, if set JobCall cannot be called internally&lt;br /&gt;
* jobout_buf contains any data sent from routine&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_PRINTER: Place a character in the printer buffer ===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* if c = 0 (printed)&lt;br /&gt;
*: The printer is on and the character was inserted&lt;br /&gt;
* if c = 1 (not printed)&lt;br /&gt;
*: The printer is off and the character was forgotten&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
== Example JobCall ==&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;DIM data% &amp;amp;1000&lt;br /&gt;
:&lt;br /&gt;
no_of_calls = 1&lt;br /&gt;
:&lt;br /&gt;
VIA = &amp;amp;E030&lt;br /&gt;
ACIA = &amp;amp;E010&lt;br /&gt;
ADC = &amp;amp;E000&lt;br /&gt;
AUX_PORT = &amp;amp;E020&lt;br /&gt;
brk_vec = &amp;amp;200&lt;br /&gt;
nmi_vec = &amp;amp;202&lt;br /&gt;
irq_vec = &amp;amp;204&lt;br /&gt;
irq2_vec = &amp;amp;206&lt;br /&gt;
sendserial_vec = &amp;amp;208&lt;br /&gt;
readserial_vec = &amp;amp;20A&lt;br /&gt;
sendjob_vec = &amp;amp;20C&lt;br /&gt;
readjob_vec = &amp;amp;20E&lt;br /&gt;
decodejob_vec = &amp;amp;210&lt;br /&gt;
unknownjob_vec = &amp;amp;212&lt;br /&gt;
extjob_vec = &amp;amp;214&lt;br /&gt;
centisec_vec = &amp;amp;216&lt;br /&gt;
internal_vec = &amp;amp;218&lt;br /&gt;
callos_vec = &amp;amp;21A&lt;br /&gt;
printer_vec = &amp;amp;21C&lt;br /&gt;
zero_gp1 = 0&lt;br /&gt;
zero_gp2 = 2&lt;br /&gt;
zero_gp3 = 4&lt;br /&gt;
zero_gp4 = 6&lt;br /&gt;
zero_gp5 = 8&lt;br /&gt;
zero_gp6 = 10&lt;br /&gt;
zero_gp7 = 12&lt;br /&gt;
zero_gp8 = 14&lt;br /&gt;
zero_gp9 = 16&lt;br /&gt;
zero_gp10 = 18&lt;br /&gt;
user_reserved = &amp;amp;70&lt;br /&gt;
irq_A = &amp;amp;A0&lt;br /&gt;
fcount = &amp;amp;A1&lt;br /&gt;
RAM_size = &amp;amp;A3&lt;br /&gt;
jobout_buf = &amp;amp;400&lt;br /&gt;
jobin_buf = &amp;amp;480&lt;br /&gt;
OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
OS_CALLOS = &amp;amp;FFBC&lt;br /&gt;
OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
:&lt;br /&gt;
FOR create=1 TO 2&lt;br /&gt;
  :&lt;br /&gt;
  FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
    P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
    [OPT pass%&lt;br /&gt;
    :&lt;br /&gt;
    .job_DemoJob&lt;br /&gt;
    CMP #1&lt;br /&gt;
    BEQ DemoJob_go&lt;br /&gt;
    LDA #1&lt;br /&gt;
    LDY #0&lt;br /&gt;
    RTS&lt;br /&gt;
    /&lt;br /&gt;
    .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    :&lt;br /&gt;
    .internal_handle&lt;br /&gt;
    CMP #1; Is it function call 1 (NameCode) ?&lt;br /&gt;
    BNE internal_handle_2; No, check for function 2&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID number&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #2; CALLOS function 2&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #1; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_2&lt;br /&gt;
    CMP #2; Is it function call 2 (CodeName) ?&lt;br /&gt;
    BNE internal_handle_3; No, unknown, pass on&lt;br /&gt;
    /&lt;br /&gt;
    LDA job_id; Get our JOB ID&lt;br /&gt;
    STA zero_gp3; Place it in zero_gp3 for CALLOS&lt;br /&gt;
    LDX #job_names MOD 256; Our Job name table (LSB)&lt;br /&gt;
    LDY #job_names DIV 256; Our Job name table (MSB)&lt;br /&gt;
    LDA #3; CALLOS function 3&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    BCC internal_yes; Found, go off and claim call&lt;br /&gt;
    LDA #2; Not found, restore A&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .internal_yes&lt;br /&gt;
    LDA #0; We want to claim call for some reason&lt;br /&gt;
    /&lt;br /&gt;
    .internal_handle_3&lt;br /&gt;
    JMP (ovec); Call back to old vector&lt;br /&gt;
    :&lt;br /&gt;
    .job_handle&lt;br /&gt;
    PHA; Make sure to preserve A&lt;br /&gt;
    CPY call; Check call wanted against base of ours&lt;br /&gt;
    BCC job_handle_no; Less than our base, so definately not ours&lt;br /&gt;
    TYA&lt;br /&gt;
    SBC #no_of_calls; Subtract number of calls we have&lt;br /&gt;
    CMP call; Now compare with base&lt;br /&gt;
    BCC job_handle2; Yes, one of ours&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle.no&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (ovec2); Call back to old vector&lt;br /&gt;
    /&lt;br /&gt;
    .job_handle2&lt;br /&gt;
    TYA&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC call; Subtract our base from call&lt;br /&gt;
    ASL A; Times by 2 for offset into table&lt;br /&gt;
    TAY; Place in Y&lt;br /&gt;
    LDA job_run_table,Y; Get LSB of routine&lt;br /&gt;
    STA zero_gp1; Store it for indirect jump (LSB)&lt;br /&gt;
    LDA job_run_table+1,Y; Get MSB of routine&lt;br /&gt;
    STA zero_gp1+1; Store it for indirect jump (MSB)&lt;br /&gt;
    PLA; Restore A&lt;br /&gt;
    JMP (zero_gp1); Call our relevant routine&lt;br /&gt;
    :&lt;br /&gt;
    .job_names; List of our JobNames&lt;br /&gt;
    EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13; Test job name&lt;br /&gt;
    /&lt;br /&gt;
    EQUB &amp;amp;FF; &amp;amp;FF - marks end of table&lt;br /&gt;
    :&lt;br /&gt;
    .job_run_table; List of routine addresses to match Jobs&lt;br /&gt;
    EQUW job_DemoJob; Test job routine&lt;br /&gt;
    :&lt;br /&gt;
    .call   : EQUB 0; Store for our JobCode base&lt;br /&gt;
    .job_id : EQUB 0; Store for our JobId&lt;br /&gt;
    .ovec   : EQUW 0; Store for old internal_vec value&lt;br /&gt;
    .ovec2  : EQUW 0; Store for old unknownjob_vec value&lt;br /&gt;
    :&lt;br /&gt;
    EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
    .end_of_code; end_of_code should be on page boundry&lt;br /&gt;
    :&lt;br /&gt;
    .execute&lt;br /&gt;
    LDX #no_of_calls; Number of JobCodes wanted&lt;br /&gt;
    LDA #1; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
    JSR OS_CALLOS; Call CALLOS&lt;br /&gt;
    STX call; Store base address of JobCodes allocated&lt;br /&gt;
    STY job_id; Store JOB ID given&lt;br /&gt;
    CPX #0; Check we were given some codes&lt;br /&gt;
    BEQ noinstall; No, don't bother installing ourself&lt;br /&gt;
    /&lt;br /&gt;
    LDA internal_vec; Get old internal_vec (LSB)&lt;br /&gt;
    STA ovec; Store it&lt;br /&gt;
    LDA internal_vec+1; Get old internal_vec (MSB)&lt;br /&gt;
    STA ovec+1; Store it&lt;br /&gt;
    LDA #internal_handle MOD 256; Our internal handler (LSB)&lt;br /&gt;
    STA internal_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #internal_handle DIV 256; Our internal handler (MSB)&lt;br /&gt;
    STA internal_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDA unknownjob_vec; Get old unknownjob_vec (LSB)&lt;br /&gt;
    STA ovec2; Store it&lt;br /&gt;
    LDA unknownjob_vec+1; Get old unknownjob_vec (MSB)&lt;br /&gt;
    STA ovec2+1; Store it&lt;br /&gt;
    LDA #job_handle MOD 256; Our unknown job handler (LSB)&lt;br /&gt;
    STA unknownjob_vec; Place it in the vector (LSB)&lt;br /&gt;
    LDA #job_handle DIV 256; Our unknown job handler (MSB)&lt;br /&gt;
    STA unknownjob_vec+1; Place it in the vector (MSB)&lt;br /&gt;
    /&lt;br /&gt;
    LDX #end_of_code MOD 256; End of code (LSB)&lt;br /&gt;
    LDY #end_of_code DIV 256; End of code (MSB)&lt;br /&gt;
    STX jobin_buf; Place it in jobin_buf (LSB)&lt;br /&gt;
    STY jobin_buf+1; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
    LDA #65; JobCode for WriteLomem&lt;br /&gt;
    JSR OS_DECODEJOB; Call JobCall handler&lt;br /&gt;
    /&lt;br /&gt;
    .noinstall&lt;br /&gt;
    RTS; End - return to OS&lt;br /&gt;
    :&lt;br /&gt;
    .execcall&lt;br /&gt;
    LDA #&amp;gt;(execute-address); Final execution address offset (LSB)&lt;br /&gt;
    STA zero_gp1&lt;br /&gt;
    LDA #&amp;lt;(execute-address); Final execution address offset (MSB)&lt;br /&gt;
    STA zero_gp1+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(BitMap-address); BitMap address offset (LSB)&lt;br /&gt;
    STA zero_gp2&lt;br /&gt;
    LDA #&amp;lt;(BitMap-address); BitMap address offset (MSB)&lt;br /&gt;
    STA zero_gp2+1&lt;br /&gt;
    /&lt;br /&gt;
    LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
    STA zero_gp3&lt;br /&gt;
    LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
    STA zero_gp3+1&lt;br /&gt;
    /&lt;br /&gt;
    TXA; Execcall REAL address&lt;br /&gt;
    SEC&lt;br /&gt;
    SBC #&amp;gt;(execcall-address); Subtract length&lt;br /&gt;
    TAX; Put it back in X&lt;br /&gt;
    TYA; Ditto for MSB&lt;br /&gt;
    SBC #&amp;lt;(execcall-address)&lt;br /&gt;
    TAY&lt;br /&gt;
    LDA #4; CALLOS function for Relocate&lt;br /&gt;
    JMP OS_CALLOS; Call CALLOS&lt;br /&gt;
    /&lt;br /&gt;
    .BitMap; BitMap start&lt;br /&gt;
    :&lt;br /&gt;
    ]&lt;br /&gt;
  NEXT&lt;br /&gt;
  :&lt;br /&gt;
  OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
  :&lt;br /&gt;
NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=273</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=273"/>
		<updated>2023-11-04T17:33:13Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Moved OS_PRINTER into OS Calls section and added header to Modules description&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = JobCode&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf should contain any input data&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = flag&lt;br /&gt;
* X = number of bytes given back by routine&lt;br /&gt;
* Y = undefined&lt;br /&gt;
* c = status, if set JobCall does not exist&lt;br /&gt;
* z = status, if set JobCall cannot be called internally&lt;br /&gt;
* jobout_buf contains any data sent from routine&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_PRINTER: Place a character in the printer buffer ===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* if c = 0 (printed)&lt;br /&gt;
*: The printer is on and the character was inserted&lt;br /&gt;
* if c = 1 (not printed)&lt;br /&gt;
*: The printer is off and the character was forgotten&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
== Example JobCall ==&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=272</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=272"/>
		<updated>2023-11-04T17:27:52Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* OS_DECODEJOB */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = JobCode&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf should contain any input data&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = flag&lt;br /&gt;
* X = number of bytes given back by routine&lt;br /&gt;
* Y = undefined&lt;br /&gt;
* c = status, if set JobCall does not exist&lt;br /&gt;
* z = status, if set JobCall cannot be called internally&lt;br /&gt;
* jobout_buf contains any data sent from routine&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=271</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=271"/>
		<updated>2023-11-04T17:26:17Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* OS_READJOB and OS_SENDJOB */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
==== OS_READJOB: Read job value from the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = byte read&lt;br /&gt;
* X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
When coming from the serial port it waits for a character to be received, if you are doing a serial only call and wish to have a loop checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDJOB: Give a job value back to the user ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = byte to give&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y is preserved&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This will send either out to the serial port or place a byte in jobout_buf.&lt;br /&gt;
For internal calls this stores the value in jobout_buf, which is allocated 128 bytes, no bound checking is done so sending more than 128 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=270</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=270"/>
		<updated>2023-11-04T17:23:01Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Serial Port */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
&lt;br /&gt;
==== OS_READBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
A, X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* X, Y preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
* if c = 1 (no character received)&lt;br /&gt;
*: A is preserved&lt;br /&gt;
* if c = 0 (character received)&lt;br /&gt;
*: A = character received&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_SENDBYTE ====&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = character to send&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A, X, Y, c is preserved&lt;br /&gt;
* z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=269</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=269"/>
		<updated>2023-11-04T17:17:56Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;hr {&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.calltable tr td {&lt;br /&gt;
	vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
table.calltable tr td &amp;gt; * {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
table.calltable tr td:first-child {&lt;br /&gt;
	font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
table.calltable tr td:first-child::after {&lt;br /&gt;
	content: ':';&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=268</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=268"/>
		<updated>2023-11-04T09:41:35Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Vectors */ Converted plaintext documentation to call tables&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use OS_CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isn't done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;A0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;A0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;A0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== internal_vec and unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two in fact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with OS_CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 0 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
Nothing&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
Leave everything as it is and exit straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used for claimed calls&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 1 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
NameCode request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (CR terminated)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobName recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** first byte of jobin_buf contains matched JobCode&lt;br /&gt;
* if JobName not recognised then&lt;br /&gt;
** A = 1&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== internal_vec 2 =====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
CodeName request&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if JobCode recognised then&lt;br /&gt;
** A = 0&lt;br /&gt;
** jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
* if JobCode not recognised then&lt;br /&gt;
** A = 2&lt;br /&gt;
** X, Y, c, z = undefined&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Purpose||&lt;br /&gt;
To pass on unknown JobCalls to the correct owner&lt;br /&gt;
|-&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = environment (0 = internal call, 1 = serial call)&lt;br /&gt;
* Y = JobCode&lt;br /&gt;
* X = JOB ID of owner&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* if jobcode yours then&lt;br /&gt;
** if wrong environment then&lt;br /&gt;
*** A = 1&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
** if right environment then&lt;br /&gt;
*** perform function&lt;br /&gt;
*** A = 0&lt;br /&gt;
*** Y = 0&lt;br /&gt;
*** X, c, z = undefined&lt;br /&gt;
* if jobcode not yours then&lt;br /&gt;
** pass on with registers unaltered&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=267</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=267"/>
		<updated>2023-11-04T09:22:29Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* OS_CALLOS */ Converted plaintext documentation to call tables&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = Hardware release version&lt;br /&gt;
* XY = OS version number&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
* Hardware version 0 is obsolete&lt;br /&gt;
* Hardware version 1 is SmartBox&lt;br /&gt;
** VIA at &amp;amp;8030&lt;br /&gt;
** UART at &amp;amp;8010&lt;br /&gt;
** ADC at &amp;amp;8000&lt;br /&gt;
** AUX_PORT (inputs) at &amp;amp;8020&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 1&lt;br /&gt;
* X = number of JobCodes required&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = base JobCode, if 0, no big enough block available&lt;br /&gt;
* Y = Job ID number, 0 if no big enough block&lt;br /&gt;
* c = set if no big enough block&lt;br /&gt;
* z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 2&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
* zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (Name not found)&lt;br /&gt;
* if c = 0 (Name found)&lt;br /&gt;
* jobin_buf contains the JobCode&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;FF&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 3&lt;br /&gt;
* XY = block pointing to block of JobNames&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* jobin_buf contains JobCode&lt;br /&gt;
* zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = undefined&lt;br /&gt;
* z is preserved&lt;br /&gt;
* if c = 1 (JobCode not found)&lt;br /&gt;
* if c = 0 (JobCode found)&lt;br /&gt;
*: jobin contains JobName (terminated by CR)&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 4&lt;br /&gt;
* XY = execution address (offset from start of code)&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
* zero_gp1 = offset address of execution after relocation&lt;br /&gt;
* zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
* zero_gp3 = length of code to relocate&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
No exit, calls begining of code straight away&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See later section on Relocation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 5&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
* XY = address of table, format as such:&lt;br /&gt;
* 1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
* byte 2+:&lt;br /&gt;
** address of VIA&lt;br /&gt;
** address of ACIA&lt;br /&gt;
** address of ADC&lt;br /&gt;
** address of AUX_PORT&lt;br /&gt;
** address of jobs_status&lt;br /&gt;
** address of jobin_buf&lt;br /&gt;
** address of jobout_buf&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use OS_CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 6&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 7&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 8&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 9&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 10&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 11&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y = value to write&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X, Y, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 12&lt;br /&gt;
* X = channel number to read (0 to 3)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = reserved for future use&lt;br /&gt;
* if c = 0 (8 bit reading)&lt;br /&gt;
** Y = reading&lt;br /&gt;
** X = undefined&lt;br /&gt;
* if c = 1 (16 bit reading)&lt;br /&gt;
** X = reading (LSB)&lt;br /&gt;
** Y = reading (MSB)&lt;br /&gt;
* z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
The A register MAY not return 0, it is guaranteed though that it will not return 12.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 13&lt;br /&gt;
* X = register (0 to 15)&lt;br /&gt;
* Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
See above&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 14&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 1&lt;br /&gt;
* Y = value read&lt;br /&gt;
* X, c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A returns 1.&lt;br /&gt;
This flags the ADC routines to start checking the sensors.&lt;br /&gt;
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 15&lt;br /&gt;
* X, Y, c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = status&lt;br /&gt;
* XY = pointer to sensor types block&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
* Status of 0 = checking finished and no futher check pending&lt;br /&gt;
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on&lt;br /&gt;
* Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
The block pointed to by XY consists of 4 bytes, each byte contains the sensor type for the appropiate sensor, a type of 0 means no sensor present&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 16&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 17&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 18&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 19&lt;br /&gt;
* X = EOR mask&lt;br /&gt;
* Y = AND mask&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* X = old mask value&lt;br /&gt;
* Y = new mask value&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;calltable&amp;quot;&lt;br /&gt;
|Entry||&lt;br /&gt;
* A = 20&lt;br /&gt;
* XY = address to set reset vector or 0 for read only&lt;br /&gt;
* c, z = undefined&lt;br /&gt;
|-&lt;br /&gt;
|Exit||&lt;br /&gt;
* A = 0&lt;br /&gt;
* XY = old reset vector address&lt;br /&gt;
* c, z is preserved&lt;br /&gt;
|-&lt;br /&gt;
|Note||&lt;br /&gt;
Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== Irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== Internal_vec and Unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== Function: 0 =====&lt;br /&gt;
Purpose:	Nothing&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;  Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 1 =====&lt;br /&gt;
Purpose:	NameCode request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 2 =====&lt;br /&gt;
Purpose:	CodeName request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=266</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=266"/>
		<updated>2023-11-04T09:05:04Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;hr {&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.calltable tr td {&lt;br /&gt;
	vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
table.calltable tr td &amp;gt; * {&lt;br /&gt;
	margin-top: 0;&lt;br /&gt;
}&lt;br /&gt;
table.calltable tr td:first-child {&lt;br /&gt;
	font-weight: bold;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=265</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=MediaWiki:Common.css&amp;diff=265"/>
		<updated>2023-11-04T09:03:13Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added calltable styles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;hr {&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.calltable tr td {&lt;br /&gt;
	vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.calltable tr td:first-child {&lt;br /&gt;
	font-weight: bold;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=264</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=264"/>
		<updated>2023-11-04T08:54:51Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* CallOS */ Restructured OS_CALLOS headers for more consistent names and to include routine description&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== OS_CALLOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 0: Return the OS version number ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 1: Claim a block of JobCodes ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 2: Handle NameCode request for the application ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 3: Handle CodeName request for the application ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 4: Relocate code ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 5: Describe the enviroment ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 6: Read a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 7: Write to a register in the VIA ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 8: Read a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 9: Write to a register in the ACIA ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 10: Read a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 11: Write to a register in the ADC ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 12: Read an ADC channel ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 13: Read a register in the AUX_PORT ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== Irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== Internal_vec and Unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== Function: 0 =====&lt;br /&gt;
Purpose:	Nothing&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;  Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 1 =====&lt;br /&gt;
Purpose:	NameCode request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 2 =====&lt;br /&gt;
Purpose:	CodeName request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=263</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=263"/>
		<updated>2023-11-04T08:46:19Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Vectors */ Put vector list into a table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== CallOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 0 ====&lt;br /&gt;
Purpose: Returns the OS version number&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 1 ====&lt;br /&gt;
Purpose: To claim a block of JobCodes&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 2 ====&lt;br /&gt;
Purpose: To handle NameCode request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 3 ====&lt;br /&gt;
Purpose: To handle CodeName request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 4 ====&lt;br /&gt;
Purpose: To relocate code&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 5 ====&lt;br /&gt;
Purpose: To &amp;quot;describe&amp;quot; the enviroment&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 6 ====&lt;br /&gt;
Purpose: To read a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 7 ====&lt;br /&gt;
Purpose: To write to a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 8 ====&lt;br /&gt;
Purpose: To read a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 9 ====&lt;br /&gt;
Purpose: To write to a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 10 ====&lt;br /&gt;
Purpose: To read a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 11 ====&lt;br /&gt;
Purpose: To write to a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 12 ====&lt;br /&gt;
Purpose: To read a ADC channel&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 13 ====&lt;br /&gt;
Purpose: To read a register in the AUX_PORT&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 14 (OS 2.066+) ====&lt;br /&gt;
Purpose: To start sensor type checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 15 (OS 2.066+) ====&lt;br /&gt;
Purpose: To check the status of sensor checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 16 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 17 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 18 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 19 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the ACIA's ctrl register and OS' soft copy&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 20 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the reset vector for battery back RAM support&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| brk_vec || &amp;amp;200&lt;br /&gt;
| Called whenever there is a BRK error, which should not occur under normal use, as the controller OS does not make use of BRK errors.&lt;br /&gt;
|-&lt;br /&gt;
| nmi_vec || &amp;amp;202&lt;br /&gt;
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).&lt;br /&gt;
|-&lt;br /&gt;
| irq_vec || &amp;amp;204&lt;br /&gt;
| Called whenever there is an interupt.&lt;br /&gt;
|-&lt;br /&gt;
| irq2_vec || &amp;amp;206&lt;br /&gt;
| Called whenever an unknown interupt is encountered, ie.  didn't come from the controller's VIA, ACIA or ADC, or the interupt from the VIA was not used by the OS.&lt;br /&gt;
|-&lt;br /&gt;
| sendserial_vec || &amp;amp;208&lt;br /&gt;
| Called when OS_SENDBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| readserial_vec || &amp;amp;20A&lt;br /&gt;
| Called when OS_READBYTE is called.&lt;br /&gt;
|-&lt;br /&gt;
| sendjob_vec || &amp;amp;20C&lt;br /&gt;
| Called when OS_SENDJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| readjob_vec || &amp;amp;20E&lt;br /&gt;
| Called when OS_READJOB is called.&lt;br /&gt;
|-&lt;br /&gt;
| decode_job_vec || &amp;amp;210&lt;br /&gt;
| Called when OS_DECODEJOB is called.  Note that OS_DECODEJOB first calls a OS routine which sets a flag to say it's been called internally, JobCode requests the OS gets from the serial port are called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| unknownjob_vec || &amp;amp;212&lt;br /&gt;
| Called whenever an unknown JobCode is encountered.&lt;br /&gt;
|-&lt;br /&gt;
| extjob_vec || &amp;amp;214&lt;br /&gt;
| Called whenever the ExtendedJob is called, the A register holds the extension value.&lt;br /&gt;
|-&lt;br /&gt;
| centisec_vec || &amp;amp;216&lt;br /&gt;
| Called 100 times a second from the IRQ routine from interupts off timer 1 of the VIA.  The OS has its pulsing routines on the end of this.  Note that you should return with a RTS, not RTI, you may also corrupt any of the registers.&lt;br /&gt;
|-&lt;br /&gt;
| internal_vec || &amp;amp;218&lt;br /&gt;
| Called when some information is needed from various parts of the system, which includes the OS which lies on the end of this vector.  An example is NameCode, which calls this vector to ask everybody if they recognise the JobName in question.&lt;br /&gt;
|-&lt;br /&gt;
| callos_vec || &amp;amp;21A&lt;br /&gt;
| Called when OS_CALLOS is called.&lt;br /&gt;
|-&lt;br /&gt;
| printer_vec || &amp;amp;21C&lt;br /&gt;
| Called when OS_PRINTER is called.&lt;br /&gt;
|-&lt;br /&gt;
| reset_vec	|| &amp;amp;21E&lt;br /&gt;
| Called when the OS gets a reset and the internal check bytes flag the integrity of the RAM.  It is first called with C cleared for everything to setup vectors and then called with C set for a foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== Irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== Internal_vec and Unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== Function: 0 =====&lt;br /&gt;
Purpose:	Nothing&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;  Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 1 =====&lt;br /&gt;
Purpose:	NameCode request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 2 =====&lt;br /&gt;
Purpose:	CodeName request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=262</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=262"/>
		<updated>2023-11-04T08:34:02Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* Memory */ Put zero page data in a table&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;00-&amp;amp;14||General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;15-&amp;amp;6F||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;70-&amp;amp;9F||Available to the user&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A0||Accumulator store for irqs (irq_A)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A1-&amp;amp;A2||Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A3||RAM size, high byte of RAM size of machine, ie. for 32k this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;A4-&amp;amp;AF||Reserved&lt;br /&gt;
|-&lt;br /&gt;
|&amp;amp;B0-&amp;amp;FF||Used by the OS&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS.&lt;br /&gt;
&lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== CallOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 0 ====&lt;br /&gt;
Purpose: Returns the OS version number&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 1 ====&lt;br /&gt;
Purpose: To claim a block of JobCodes&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 2 ====&lt;br /&gt;
Purpose: To handle NameCode request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 3 ====&lt;br /&gt;
Purpose: To handle CodeName request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 4 ====&lt;br /&gt;
Purpose: To relocate code&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 5 ====&lt;br /&gt;
Purpose: To &amp;quot;describe&amp;quot; the enviroment&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 6 ====&lt;br /&gt;
Purpose: To read a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 7 ====&lt;br /&gt;
Purpose: To write to a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 8 ====&lt;br /&gt;
Purpose: To read a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 9 ====&lt;br /&gt;
Purpose: To write to a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 10 ====&lt;br /&gt;
Purpose: To read a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 11 ====&lt;br /&gt;
Purpose: To write to a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 12 ====&lt;br /&gt;
Purpose: To read a ADC channel&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 13 ====&lt;br /&gt;
Purpose: To read a register in the AUX_PORT&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 14 (OS 2.066+) ====&lt;br /&gt;
Purpose: To start sensor type checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 15 (OS 2.066+) ====&lt;br /&gt;
Purpose: To check the status of sensor checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 16 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 17 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 18 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 19 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the ACIA's ctrl register and OS' soft copy&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 20 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the reset vector for battery back RAM support&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
* brk_vec	at address &amp;amp;200&lt;br /&gt;
* nmi_vec	at address &amp;amp;202&lt;br /&gt;
* irq_vec	at address &amp;amp;204&lt;br /&gt;
* irq2_vec	at address &amp;amp;206&lt;br /&gt;
* sendserial_vec	at address &amp;amp;208&lt;br /&gt;
* readserial_vec	at address &amp;amp;20A&lt;br /&gt;
* sendjob_vec	at address &amp;amp;20C&lt;br /&gt;
* readjob_vec	at address &amp;amp;20E&lt;br /&gt;
* decode_job_vec	at address &amp;amp;210&lt;br /&gt;
* unknownjob_vec	at address &amp;amp;212&lt;br /&gt;
* extjob_vec	at address &amp;amp;214&lt;br /&gt;
* centisec_vec	at address &amp;amp;216&lt;br /&gt;
* internal_vec	at address &amp;amp;218&lt;br /&gt;
* callos_vec	at address &amp;amp;21A&lt;br /&gt;
* printer_vec	at address &amp;amp;21C&lt;br /&gt;
* reset_vec	at address &amp;amp;21E&lt;br /&gt;
&lt;br /&gt;
The brk_vec is called whenever there is a BRK error, which should&lt;br /&gt;
not occur under normal use, as the controller OS does not make use of BRK&lt;br /&gt;
errors.&lt;br /&gt;
&lt;br /&gt;
The nmi_vec is called whenever there is a NMI request (not really&lt;br /&gt;
applicable, as the NMI line is not connected to anything).&lt;br /&gt;
&lt;br /&gt;
The irq_vec is called whenever there is an interupt.&lt;br /&gt;
&lt;br /&gt;
The irq2_vec is called whenever an unknown interupt is encountered,&lt;br /&gt;
ie.  didn't come from the controllerXs VIA, ACIA or ADC, or the interupt&lt;br /&gt;
from the VIA was not used by the OS.&lt;br /&gt;
&lt;br /&gt;
The sendserial_vec is called when OS_SENDBYTE is called.&lt;br /&gt;
&lt;br /&gt;
The readserial_vec is called when OS_READBYTE is called.&lt;br /&gt;
&lt;br /&gt;
The sendjob_vec is called when OS_SENDJOB is called.&lt;br /&gt;
&lt;br /&gt;
The readjob_vec is called when OS_READJOB is called.&lt;br /&gt;
&lt;br /&gt;
The decodejob_vec is called when OS_DECODEJOB is called.  Note that&lt;br /&gt;
OS_DECODEJOB first calls a OS routine which sets a flag to say it's been&lt;br /&gt;
called internally, JobCode requests the OS gets from the serial port are&lt;br /&gt;
called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The unknownjob_vec is called whenever an unknown JobCode is&lt;br /&gt;
encountered.&lt;br /&gt;
&lt;br /&gt;
The extendedjob_vec is called whenever the ExtendedJob is called,&lt;br /&gt;
the A register holds the extension value.&lt;br /&gt;
&lt;br /&gt;
The centisecond_vec is called 100 times a second from the IRQ&lt;br /&gt;
routine from interupts off timer 1 of the VIA.  The OS has its pulsing&lt;br /&gt;
routines on the end of this.  Note that you should return with a RTS, not&lt;br /&gt;
RTI, you may also corrupt any of the registers.&lt;br /&gt;
&lt;br /&gt;
The internal_vec is called when some information is needed from&lt;br /&gt;
various parts of the system, which includes the OS whch lies on the end of&lt;br /&gt;
this vector.  An example is NameCode, which calls this vector to ask&lt;br /&gt;
everybody if they recognise the JobName in question.&lt;br /&gt;
&lt;br /&gt;
The callos_vec is called when OS_CALLOS is called.&lt;br /&gt;
&lt;br /&gt;
The printer_vec is called when OS_PRINTER is called.&lt;br /&gt;
&lt;br /&gt;
The reset_vec is called when the OS gets a reset and the internal&lt;br /&gt;
check bytes flag the integrity of the RAM.  It is first called with C&lt;br /&gt;
cleared for everything to setup vectors and then called with C set for a&lt;br /&gt;
foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this&lt;br /&gt;
vector.&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== Irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== Internal_vec and Unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== Function: 0 =====&lt;br /&gt;
Purpose:	Nothing&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;  Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 1 =====&lt;br /&gt;
Purpose:	NameCode request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 2 =====&lt;br /&gt;
Purpose:	CodeName request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=261</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=261"/>
		<updated>2023-11-03T22:51:45Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Did rough conversion of plain text to MediaWiki markup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Documentation for SmartBox OS 2.066 ==&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
&lt;br /&gt;
There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
== Call types ==&lt;br /&gt;
&lt;br /&gt;
There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
=== Simple stand alone routine ===&lt;br /&gt;
&lt;br /&gt;
Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
=== Extended JobCall  ===&lt;br /&gt;
&lt;br /&gt;
There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
=== Full JobCall ===&lt;br /&gt;
&lt;br /&gt;
This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	0	General workspace, used to pass parameters to some OS&lt;br /&gt;
			routines, can also be used by other routines DURING&lt;br /&gt;
	14		their execution.&lt;br /&gt;
	15	Reserved&lt;br /&gt;
	6f&lt;br /&gt;
	70	Available to the user&lt;br /&gt;
	9f&lt;br /&gt;
	a0	Accumulator store for irqs (irq_A)&lt;br /&gt;
	a1	Two byte counter, decremented at 100hz, useful for temporary&lt;br /&gt;
	a2		timing purposes (fcount)&lt;br /&gt;
	a3	RAM size, high byte of RAM size of machine, ie. for 32k&lt;br /&gt;
			this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
	a4	Reserved&lt;br /&gt;
	af&lt;br /&gt;
	b0	Used by the OS&lt;br /&gt;
	ff&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
* &amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also contains the vector table, more on that later.&lt;br /&gt;
* &amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
* &amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
* &amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
* &amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
* &amp;amp;600 to the end of RAM memory is for the user. The user should not assume what the top of AVAILABLE memory is and use the system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS. &lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
== OS Calls ==&lt;br /&gt;
&lt;br /&gt;
The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
* OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
* OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
* OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
* OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
* OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
* OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
* OS_PRINTER is reserved for future use.&lt;br /&gt;
* OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
* OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
* OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
* OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
* OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
* OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
=== Serial Port ===&lt;br /&gt;
&lt;br /&gt;
The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_READJOB and OS_SENDJOB ===&lt;br /&gt;
&lt;br /&gt;
To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== OS_DECODEJOB ===&lt;br /&gt;
&lt;br /&gt;
JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
=== CallOS ===&lt;br /&gt;
&lt;br /&gt;
This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 0 ====&lt;br /&gt;
Purpose: Returns the OS version number&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 1 ====&lt;br /&gt;
Purpose: To claim a block of JobCodes&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 2 ====&lt;br /&gt;
Purpose: To handle NameCode request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 3 ====&lt;br /&gt;
Purpose: To handle CodeName request for the application.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 4 ====&lt;br /&gt;
Purpose: To relocate code&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 5 ====&lt;br /&gt;
Purpose: To &amp;quot;describe&amp;quot; the enviroment&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 6 ====&lt;br /&gt;
Purpose: To read a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 7 ====&lt;br /&gt;
Purpose: To write to a register in the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 8 ====&lt;br /&gt;
Purpose: To read a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 9 ====&lt;br /&gt;
Purpose: To write to a register in the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 10 ====&lt;br /&gt;
Purpose: To read a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 11 ====&lt;br /&gt;
Purpose: To write to a register in the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 12 ====&lt;br /&gt;
Purpose: To read a ADC channel&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 13 ====&lt;br /&gt;
Purpose: To read a register in the AUX_PORT&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 14 (OS 2.066+) ====&lt;br /&gt;
Purpose: To start sensor type checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 15 (OS 2.066+) ====&lt;br /&gt;
Purpose: To check the status of sensor checking&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 16 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the VIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 17 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ACIA&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 18 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the OS' irq mask for the ADC&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 19 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the ACIA's ctrl register and OS' soft copy&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CALLOS	: 20 (OS 2.069+) ====&lt;br /&gt;
Purpose: To set the reset vector for battery back RAM support&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Vectors ==&lt;br /&gt;
&lt;br /&gt;
Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
* brk_vec	at address &amp;amp;200&lt;br /&gt;
* nmi_vec	at address &amp;amp;202&lt;br /&gt;
* irq_vec	at address &amp;amp;204&lt;br /&gt;
* irq2_vec	at address &amp;amp;206&lt;br /&gt;
* sendserial_vec	at address &amp;amp;208&lt;br /&gt;
* readserial_vec	at address &amp;amp;20A&lt;br /&gt;
* sendjob_vec	at address &amp;amp;20C&lt;br /&gt;
* readjob_vec	at address &amp;amp;20E&lt;br /&gt;
* decode_job_vec	at address &amp;amp;210&lt;br /&gt;
* unknownjob_vec	at address &amp;amp;212&lt;br /&gt;
* extjob_vec	at address &amp;amp;214&lt;br /&gt;
* centisec_vec	at address &amp;amp;216&lt;br /&gt;
* internal_vec	at address &amp;amp;218&lt;br /&gt;
* callos_vec	at address &amp;amp;21A&lt;br /&gt;
* printer_vec	at address &amp;amp;21C&lt;br /&gt;
* reset_vec	at address &amp;amp;21E&lt;br /&gt;
&lt;br /&gt;
The brk_vec is called whenever there is a BRK error, which should&lt;br /&gt;
not occur under normal use, as the controller OS does not make use of BRK&lt;br /&gt;
errors.&lt;br /&gt;
&lt;br /&gt;
The nmi_vec is called whenever there is a NMI request (not really&lt;br /&gt;
applicable, as the NMI line is not connected to anything).&lt;br /&gt;
&lt;br /&gt;
The irq_vec is called whenever there is an interupt.&lt;br /&gt;
&lt;br /&gt;
The irq2_vec is called whenever an unknown interupt is encountered,&lt;br /&gt;
ie.  didn't come from the controllerXs VIA, ACIA or ADC, or the interupt&lt;br /&gt;
from the VIA was not used by the OS.&lt;br /&gt;
&lt;br /&gt;
The sendserial_vec is called when OS_SENDBYTE is called.&lt;br /&gt;
&lt;br /&gt;
The readserial_vec is called when OS_READBYTE is called.&lt;br /&gt;
&lt;br /&gt;
The sendjob_vec is called when OS_SENDJOB is called.&lt;br /&gt;
&lt;br /&gt;
The readjob_vec is called when OS_READJOB is called.&lt;br /&gt;
&lt;br /&gt;
The decodejob_vec is called when OS_DECODEJOB is called.  Note that&lt;br /&gt;
OS_DECODEJOB first calls a OS routine which sets a flag to say it's been&lt;br /&gt;
called internally, JobCode requests the OS gets from the serial port are&lt;br /&gt;
called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The unknownjob_vec is called whenever an unknown JobCode is&lt;br /&gt;
encountered.&lt;br /&gt;
&lt;br /&gt;
The extendedjob_vec is called whenever the ExtendedJob is called,&lt;br /&gt;
the A register holds the extension value.&lt;br /&gt;
&lt;br /&gt;
The centisecond_vec is called 100 times a second from the IRQ&lt;br /&gt;
routine from interupts off timer 1 of the VIA.  The OS has its pulsing&lt;br /&gt;
routines on the end of this.  Note that you should return with a RTS, not&lt;br /&gt;
RTI, you may also corrupt any of the registers.&lt;br /&gt;
&lt;br /&gt;
The internal_vec is called when some information is needed from&lt;br /&gt;
various parts of the system, which includes the OS whch lies on the end of&lt;br /&gt;
this vector.  An example is NameCode, which calls this vector to ask&lt;br /&gt;
everybody if they recognise the JobName in question.&lt;br /&gt;
&lt;br /&gt;
The callos_vec is called when OS_CALLOS is called.&lt;br /&gt;
&lt;br /&gt;
The printer_vec is called when OS_PRINTER is called.&lt;br /&gt;
&lt;br /&gt;
The reset_vec is called when the OS gets a reset and the internal&lt;br /&gt;
check bytes flag the integrity of the RAM.  It is first called with C&lt;br /&gt;
cleared for everything to setup vectors and then called with C set for a&lt;br /&gt;
foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this&lt;br /&gt;
vector.&lt;br /&gt;
&lt;br /&gt;
All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
&lt;br /&gt;
unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
&lt;br /&gt;
Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
=== Irq_vec ===&lt;br /&gt;
&lt;br /&gt;
This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
=== Internal_vec and Unknownjob_vec ===&lt;br /&gt;
&lt;br /&gt;
The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
==== internal_vec ====&lt;br /&gt;
&lt;br /&gt;
===== Function: 0 =====&lt;br /&gt;
Purpose:	Nothing&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;  Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 1 =====&lt;br /&gt;
Purpose:	NameCode request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Function: 2 =====&lt;br /&gt;
Purpose:	CodeName request&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
==== unknownjob_vec ====&lt;br /&gt;
Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Starting Up ==&lt;br /&gt;
&lt;br /&gt;
The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
# Claim the number of JobCodes you want&lt;br /&gt;
#:		if success:&lt;br /&gt;
# patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
# move LOMEM up to protect your program&lt;br /&gt;
# do anything else for initial startup you may want to do&lt;br /&gt;
# return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
This in code looks like:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don't bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relocation ==&lt;br /&gt;
&lt;br /&gt;
The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
== Layout Summary ==&lt;br /&gt;
&lt;br /&gt;
The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
== Other miscellaneous calls ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
&lt;br /&gt;
For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=260</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=260"/>
		<updated>2023-11-03T22:10:51Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Moved changelog below the documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; &amp;lt;nowiki&amp;gt;		Documentation for SmartBox OS 2.066&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Preview&lt;br /&gt;
&lt;br /&gt;
	There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
	One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Call types&lt;br /&gt;
&lt;br /&gt;
	There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
	a) Simple stand alone routine.  Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
	b) Extended JobCall.  There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
	c) Full JobCall.  This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
	Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Memory&lt;br /&gt;
&lt;br /&gt;
	The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
	0	General workspace, used to pass parameters to some OS&lt;br /&gt;
			routines, can also be used by other routines DURING&lt;br /&gt;
	14		their execution.&lt;br /&gt;
	15	Reserved&lt;br /&gt;
	6f&lt;br /&gt;
	70	Available to the user&lt;br /&gt;
	9f&lt;br /&gt;
	a0	Accumulator store for irqs (irq_A)&lt;br /&gt;
	a1	Two byte counter, decremented at 100hz, useful for temporary&lt;br /&gt;
	a2		timing purposes (fcount)&lt;br /&gt;
	a3	RAM size, high byte of RAM size of machine, ie. for 32k&lt;br /&gt;
			this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
	a4	Reserved&lt;br /&gt;
	af&lt;br /&gt;
	b0	Used by the OS&lt;br /&gt;
	ff&lt;br /&gt;
&lt;br /&gt;
	The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
	A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also&lt;br /&gt;
contains the vector table, more on that later.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;600 to the end of RAM memory is for the user. The user should not&lt;br /&gt;
		assume what the top of AVAILABLE memory is and use the&lt;br /&gt;
		system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
	In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
	So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS. &lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
	There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS Calls&lt;br /&gt;
&lt;br /&gt;
	 The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
 OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
 OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
	The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
	OS_PRINTER is reserved for future use.&lt;br /&gt;
	OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
	OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
	OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
	OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
	OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
	OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
	Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Serial Port&lt;br /&gt;
&lt;br /&gt;
	The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
	Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
	As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&lt;br /&gt;
&lt;br /&gt;
	OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS_READJOB and OS_SENDJOB&lt;br /&gt;
&lt;br /&gt;
	To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS_DECODEJOB&lt;br /&gt;
&lt;br /&gt;
	 JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
	The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CallOS&lt;br /&gt;
&lt;br /&gt;
	This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
	As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 0&lt;br /&gt;
 Purpose: Returns the OS version number&lt;br /&gt;
 Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 1&lt;br /&gt;
 Purpose: To claim a block of JobCodes&lt;br /&gt;
 Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 2&lt;br /&gt;
 Purpose: To handle NameCode request for the application.&lt;br /&gt;
 Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 3&lt;br /&gt;
 Purpose: To handle CodeName request for the application.&lt;br /&gt;
 Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 4&lt;br /&gt;
 Purpose: To relocate code&lt;br /&gt;
 Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 5&lt;br /&gt;
 Purpose: To &amp;quot;describe&amp;quot; the enviroment&lt;br /&gt;
 Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 6&lt;br /&gt;
 Purpose: To read a register in the VIA&lt;br /&gt;
 Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 7&lt;br /&gt;
 Purpose: To write to a register in the VIA&lt;br /&gt;
 Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 8&lt;br /&gt;
 Purpose: To read a register in the ACIA&lt;br /&gt;
 Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 9&lt;br /&gt;
 Purpose: To write to a register in the ACIA&lt;br /&gt;
 Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 10&lt;br /&gt;
 Purpose: To read a register in the ADC&lt;br /&gt;
 Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 11&lt;br /&gt;
 Purpose: To write to a register in the ADC&lt;br /&gt;
 Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 12&lt;br /&gt;
 Purpose: To read a ADC channel&lt;br /&gt;
 Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 13&lt;br /&gt;
 Purpose: To read a register in the AUX_PORT&lt;br /&gt;
 Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 14 (OS 2.066+)&lt;br /&gt;
 Purpose: To start sensor type checking&lt;br /&gt;
 Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 15 (OS 2.066+)&lt;br /&gt;
 Purpose: To check the status of sensor checking&lt;br /&gt;
 Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 16 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the VIA&lt;br /&gt;
 Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 17 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the ACIA&lt;br /&gt;
 Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 18 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the ADC&lt;br /&gt;
 Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 19 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the ACIA's ctrl register and OS' soft copy&lt;br /&gt;
 Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 20 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the reset vector for battery back RAM support&lt;br /&gt;
 Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vectors&lt;br /&gt;
&lt;br /&gt;
	Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
	The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
 brk_vec	at address &amp;amp;200&lt;br /&gt;
 nmi_vec	at address &amp;amp;202&lt;br /&gt;
 irq_vec	at address &amp;amp;204&lt;br /&gt;
 irq2_vec	at address &amp;amp;206&lt;br /&gt;
 sendserial_vec	at address &amp;amp;208&lt;br /&gt;
 readserial_vec	at address &amp;amp;20A&lt;br /&gt;
 sendjob_vec	at address &amp;amp;20C&lt;br /&gt;
 readjob_vec	at address &amp;amp;20E&lt;br /&gt;
 decode_job_vec	at address &amp;amp;210&lt;br /&gt;
 unknownjob_vec	at address &amp;amp;212&lt;br /&gt;
 extjob_vec	at address &amp;amp;214&lt;br /&gt;
 centisec_vec	at address &amp;amp;216&lt;br /&gt;
 internal_vec	at address &amp;amp;218&lt;br /&gt;
 callos_vec	at address &amp;amp;21A&lt;br /&gt;
 printer_vec	at address &amp;amp;21C&lt;br /&gt;
 reset_vec	at address &amp;amp;21E&lt;br /&gt;
&lt;br /&gt;
	The brk_vec is called whenever there is a BRK error, which should&lt;br /&gt;
not occur under normal use, as the controller OS does not make use of BRK&lt;br /&gt;
errors.&lt;br /&gt;
	The nmi_vec is called whenever there is a NMI request (not really&lt;br /&gt;
applicable, as the NMI line is not connected to anything).&lt;br /&gt;
	The irq_vec is called whenever there is an interupt.&lt;br /&gt;
	The irq2_vec is called whenever an unknown interupt is encountered,&lt;br /&gt;
ie.  didnXt come from the controllerXs VIA, ACIA or ADC, or the interupt&lt;br /&gt;
from the VIA was not used by the OS.&lt;br /&gt;
	The sendserial_vec is called when OS_SENDBYTE is called.&lt;br /&gt;
	The readserial_vec is called when OS_READBYTE is called.&lt;br /&gt;
	The sendjob_vec is called when OS_SENDJOB is called.&lt;br /&gt;
	The readjob_vec is called when OS_READJOB is called.&lt;br /&gt;
	The decodejob_vec is called when OS_DECODEJOB is called.  Note that&lt;br /&gt;
OS_DECODEJOB first calls a OS routine which sets a flag to say it's been&lt;br /&gt;
called internally, JobCode requests the OS gets from the serial port are&lt;br /&gt;
called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
	The unknownjob_vec is called whenever an unknown JobCode is&lt;br /&gt;
encountered.&lt;br /&gt;
	The extendedjob_vec is called whenever the ExtendedJob is called,&lt;br /&gt;
the A register holds the extension value.&lt;br /&gt;
	The centisecond_vec is called 100 times a second from the IRQ&lt;br /&gt;
routine from interupts off timer 1 of the VIA.  The OS has its pulsing&lt;br /&gt;
routines on the end of this.  Note that you should return with a RTS, not&lt;br /&gt;
RTI, you may also corrupt any of the registers.&lt;br /&gt;
	The internal_vec is called when some information is needed from&lt;br /&gt;
various parts of the system, which includes the OS whch lies on the end of&lt;br /&gt;
this vector.  An example is NameCode, which calls this vector to ask&lt;br /&gt;
everybody if they recognise the JobName in question.&lt;br /&gt;
	The callos_vec is called when OS_CALLOS is called.&lt;br /&gt;
	The printer_vec is called when OS_PRINTER is called.&lt;br /&gt;
	The reset_vec is called when the OS gets a reset and the internal&lt;br /&gt;
check bytes flag the integrity of the RAM.  It is first called with C&lt;br /&gt;
cleared for everything to setup vectors and then called with C set for a&lt;br /&gt;
foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this&lt;br /&gt;
vector.&lt;br /&gt;
	All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
	unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
	Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Irq_vec&lt;br /&gt;
&lt;br /&gt;
	This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
	irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Internal_vec and Unknownjob_vec&lt;br /&gt;
&lt;br /&gt;
	The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
	The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
 internal_vec&lt;br /&gt;
&lt;br /&gt;
 Function: 0&lt;br /&gt;
 Purpose:	Nothing&lt;br /&gt;
 Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&lt;br /&gt;
&lt;br /&gt;
 Function: 1&lt;br /&gt;
 Purpose:	NameCode request&lt;br /&gt;
 Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 Function: 2&lt;br /&gt;
 Purpose:	CodeName request&lt;br /&gt;
 Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
	All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
 unknownjob_vec&lt;br /&gt;
&lt;br /&gt;
 Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Starting Up&lt;br /&gt;
&lt;br /&gt;
	The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
	a) Claim the number of JobCodes you want&lt;br /&gt;
		if success:&lt;br /&gt;
	b) patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
	c) move LOMEM up to protect your program&lt;br /&gt;
	d) do anything else for initial startup you may want to do&lt;br /&gt;
	e) return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
  This in code looks like:&lt;br /&gt;
&lt;br /&gt;
execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don�t bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relocation&lt;br /&gt;
&lt;br /&gt;
	The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
	The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
	All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&lt;br /&gt;
&lt;br /&gt;
	This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Layout Summary&lt;br /&gt;
&lt;br /&gt;
	The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&lt;br /&gt;
&lt;br /&gt;
	Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Other miscellaneous calls:&lt;br /&gt;
&lt;br /&gt;
Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
  For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&lt;br /&gt;
&lt;br /&gt;
  The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
  The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&lt;br /&gt;
&lt;br /&gt;
  On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
  For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_AlbertLink&amp;diff=259</id>
		<title>SmartBox AlbertLink</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_AlbertLink&amp;diff=259"/>
		<updated>2023-11-01T04:39:58Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Created page with &amp;quot; &amp;lt;nowiki&amp;gt;AlbertLink Link Protocol (Release 11)   Startup   After AL has been downloaded and called the first thing back will be a engine release number (one byte), and known setup flags (one byte) which you should check that the &amp;quot;new&amp;quot; flags you want are set. If the engine number is wrong or the setup flags you want aren't set then send a 0 and AlbertLink will quit itself, else send ANYTHING but a 0. Then send your setup flags (one byte), which will engage the various &amp;quot;ne...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; &amp;lt;nowiki&amp;gt;AlbertLink Link Protocol (Release 11)&lt;br /&gt;
&lt;br /&gt;
 Startup&lt;br /&gt;
&lt;br /&gt;
 After AL has been downloaded and called the first thing back will be a engine release number (one byte), and known setup flags (one byte) which you should check that the &amp;quot;new&amp;quot; flags you want are set. If the engine number is wrong or the setup flags you want aren't set then send a 0 and AlbertLink will quit itself, else send ANYTHING but a 0. Then send your setup flags (one byte), which will engage the various &amp;quot;new&amp;quot; options.&lt;br /&gt;
&lt;br /&gt;
ie:&lt;br /&gt;
        send jobcode for AlbertLink     &amp;gt;&lt;br /&gt;
                                        &amp;lt;        engine number&lt;br /&gt;
                                        &amp;lt;        known setup flags&lt;br /&gt;
        flag for continue or not        &amp;gt;&lt;br /&gt;
        setup flags wanted              &amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Setup Flags&lt;br /&gt;
&lt;br /&gt;
 The 8 setup flags are setup as such:&lt;br /&gt;
        bit       if set&lt;br /&gt;
         0          use new trace system&lt;br /&gt;
         1          enable procedure/label change checking&lt;br /&gt;
         2          enable custom commands&lt;br /&gt;
         3          enable prompt&lt;br /&gt;
         4          shit computer&lt;br /&gt;
         5          enable &amp;quot;get&amp;quot; line count&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Idle&lt;br /&gt;
&lt;br /&gt;
 At this point the system is idling, both ends are waiting to originate a &amp;quot;event&amp;quot; or receive one.&lt;br /&gt;
&lt;br /&gt;
 The remote end should check the serial port as often as possible, if it detects a byte then it should read it, check it is in range, and transmit a 0 (byte) back, then it should do one of the following &amp;quot;event&amp;quot; types according to the byte it received:&lt;br /&gt;
&lt;br /&gt;
File            1 +byte +string&lt;br /&gt;
        note    byte = channel reference (1 to 10)&lt;br /&gt;
        note    string = file name&lt;br /&gt;
        note    the file should then be opened and a FileBack event made or a&lt;br /&gt;
                Error event, to signal a error&lt;br /&gt;
&lt;br /&gt;
Close           2 +byte&lt;br /&gt;
        note    byte = channel reference&lt;br /&gt;
&lt;br /&gt;
Store           3 +byte +data (terminated by NUL)&lt;br /&gt;
        note    byte = channel reference&lt;br /&gt;
        note    data = data to put to file&lt;br /&gt;
&lt;br /&gt;
Trace                   old:&lt;br /&gt;
                4 +string&lt;br /&gt;
        note    string = string to print&lt;br /&gt;
                        new:&lt;br /&gt;
                4 +string +double byte&lt;br /&gt;
        note    string = procedure name&lt;br /&gt;
        note    double byte = line number&lt;br /&gt;
        return  byte&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        : 0, no stepping&lt;br /&gt;
                        : 1, wait for tracecont&lt;br /&gt;
&lt;br /&gt;
Print           5 +[characters terminated by NUL, CR expand to CRLF]&lt;br /&gt;
        note    do not CRLF after this, keep cursor position&lt;br /&gt;
&lt;br /&gt;
Error           6 +string +string2 +string3       (to change)&lt;br /&gt;
        note    string = procedure name (blank if from cmd line)&lt;br /&gt;
        note    string2 = error&lt;br /&gt;
        note    string3 = line containing error (blank if from cmd line)&lt;br /&gt;
&lt;br /&gt;
Ask             7 +byte&lt;br /&gt;
        note    byte = 'S', 'N' or 'T' (input type)&lt;br /&gt;
        return  use AskBack to return input string&lt;br /&gt;
&lt;br /&gt;
Inkey           8&lt;br /&gt;
        return  byte = 0 -&amp;gt; no key, #0 -&amp;gt; key value&lt;br /&gt;
&lt;br /&gt;
Cmd             9&lt;br /&gt;
        note    this means that command mode is ready&lt;br /&gt;
&lt;br /&gt;
Build           10 +string&lt;br /&gt;
        note    string = proc to edit&lt;br /&gt;
&lt;br /&gt;
Edit            11 +string&lt;br /&gt;
        note    string = proc to edit&lt;br /&gt;
&lt;br /&gt;
Quit            12&lt;br /&gt;
        note    user has typed QUIT, use Quit to quit system&lt;br /&gt;
&lt;br /&gt;
TraceFl         13 +byte&lt;br /&gt;
        note    byte = trace flag (0 -&amp;gt; off, #0 -&amp;gt; on)&lt;br /&gt;
&lt;br /&gt;
Load            14 +string&lt;br /&gt;
        note    string = filename&lt;br /&gt;
        note    use Put&lt;br /&gt;
        note    use Error for filing errors&lt;br /&gt;
        note    string can be blank&lt;br /&gt;
&lt;br /&gt;
Save            15 +string +string2&lt;br /&gt;
        note    string = filename&lt;br /&gt;
        note    string2 = procedure to save (or blank for all)&lt;br /&gt;
        note    use Error for filing errors&lt;br /&gt;
        note    use Get/List to get procedures&lt;br /&gt;
&lt;br /&gt;
Control         16 +byte&lt;br /&gt;
        note    byte = control parameter, if out of range, return 0 and then&lt;br /&gt;
                use Error&lt;br /&gt;
        return  byte&lt;br /&gt;
        note    byte = value of control option&lt;br /&gt;
&lt;br /&gt;
Rtc             17&lt;br /&gt;
        return  byte +byte2 +byte3 +byte4&lt;br /&gt;
        note    byte = hours&lt;br /&gt;
        note    byte2 = minutes&lt;br /&gt;
        note    byte3 = seconds&lt;br /&gt;
        note    byte4 = centiseconds&lt;br /&gt;
        note    cause Error AFTER returning 0 for all if not supported.&lt;br /&gt;
&lt;br /&gt;
Printer         18&lt;br /&gt;
        note    as Print&lt;br /&gt;
&lt;br /&gt;
Altered         19 +byte&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        bit 0 : procedure list changed&lt;br /&gt;
                        bit 1 : labels changed&lt;br /&gt;
        note    when you get this you should use List and/or ReadLabels to&lt;br /&gt;
                update yourself&lt;br /&gt;
&lt;br /&gt;
Custom          20 +data (NUL terminated)&lt;br /&gt;
&lt;br /&gt;
Custom2         21 +data (NUL terminated)&lt;br /&gt;
&lt;br /&gt;
Customfn        22 +string&lt;br /&gt;
        return  byte&lt;br /&gt;
        note    byte = fn value&lt;br /&gt;
&lt;br /&gt;
Prompt          23 +string&lt;br /&gt;
        note    string = prompt string&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 NB: &amp;quot;string&amp;quot; is a group of characters terminated by CR.&lt;br /&gt;
&lt;br /&gt;
 Any other codes should be ignored.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Remote &amp;quot;events&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 The remote end also has &amp;quot;events&amp;quot; which it can originate, to start an event, transmit the &amp;quot;event&amp;quot; code and wait until you receive a 0 (byte), ignore all other bytes received (the remote end has priority) and then you can transmit any other bytes needed:&lt;br /&gt;
&lt;br /&gt;
Setup           1 +byte&lt;br /&gt;
        note    byte = setup byte&lt;br /&gt;
        note    as startup setup flag&lt;br /&gt;
&lt;br /&gt;
List            2&lt;br /&gt;
        return  [string .....] until string is blank&lt;br /&gt;
        note    this returns a list of procedures terminated by a blank&lt;br /&gt;
&lt;br /&gt;
NameCode        3 +string&lt;br /&gt;
        note    *NB* doesn't return normal link 0 acknowledge.&lt;br /&gt;
        note    Emulates the normal OS NameCode, quiting AlbertLink if the&lt;br /&gt;
                remote doesn't try to check for AlbertLink and going out&lt;br /&gt;
                of sleep if it does try for AlbertLink&lt;br /&gt;
&lt;br /&gt;
Get             4 +byte +string&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                          0 : do not use labels&lt;br /&gt;
                          1 : use labels&lt;br /&gt;
                        255 : use LBLS setting&lt;br /&gt;
                        old:&lt;br /&gt;
        return  byte = 0 -&amp;gt; no such procedure else procedure terminated by 0ffh&lt;br /&gt;
                        new:&lt;br /&gt;
        return  byte = 0 -&amp;gt; no such procedure&lt;br /&gt;
                       1 +byte2 +byte3 +procedure -&amp;gt; procedure found&lt;br /&gt;
        note    byte2/3 is number of lines in procedure&lt;br /&gt;
&lt;br /&gt;
Put             5 +string +[procedure strings terminated by 0ffh]&lt;br /&gt;
        return  byte = 0 -&amp;gt; ok, 1 -&amp;gt; bad name, 2 -) no room 3 -&amp;gt; bad data&lt;br /&gt;
&lt;br /&gt;
Escape          6&lt;br /&gt;
        note    causes escape condition&lt;br /&gt;
&lt;br /&gt;
Quit            7&lt;br /&gt;
        note    causes AlbertLink to quit&lt;br /&gt;
&lt;br /&gt;
Cmd             8 +string&lt;br /&gt;
        note    performs a raw command, use only after flagged cmd ready else&lt;br /&gt;
                any running procedure will be stopped&lt;br /&gt;
&lt;br /&gt;
GetPorts        9&lt;br /&gt;
        return  byte = run mode (1 = running procedure)&lt;br /&gt;
                byte = inputs&lt;br /&gt;
                byte = outputs&lt;br /&gt;
                byte = motors&lt;br /&gt;
                byte = adc 1&lt;br /&gt;
                byte = adc 2&lt;br /&gt;
                byte = adc 3&lt;br /&gt;
                byte = adc 4&lt;br /&gt;
                byte = clock (hours)&lt;br /&gt;
                byte = clock&lt;br /&gt;
                byte = clock&lt;br /&gt;
                byte = clock (cs)&lt;br /&gt;
&lt;br /&gt;
GetPS           10&lt;br /&gt;
        return  data as GetPorts&lt;br /&gt;
        return  byte = sensor a id&lt;br /&gt;
                byte = sensor b id&lt;br /&gt;
                byte = sensor c id&lt;br /&gt;
                byte = sensor d id&lt;br /&gt;
        note    perform once a second or so to re-check the sensors&lt;br /&gt;
&lt;br /&gt;
SteadyLine      11 +byte +string&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                          0 : do not use labels&lt;br /&gt;
                          1 : use labels&lt;br /&gt;
                        255 : use LBLS setting&lt;br /&gt;
        return  string = expanded line&lt;br /&gt;
        note    use in editor to expanded abbreviated commands&lt;br /&gt;
&lt;br /&gt;
TraceFl         12 +byte&lt;br /&gt;
        note    byte = new trace setting and causes Trace &amp;quot;events&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SetPort         13 +byte +byte2&lt;br /&gt;
        note    byte = bits to set&lt;br /&gt;
        note    byte2 = bits to mask&lt;br /&gt;
&lt;br /&gt;
Error           14 +string&lt;br /&gt;
        note    string = error to cause&lt;br /&gt;
&lt;br /&gt;
Version         15&lt;br /&gt;
        return  string = version string&lt;br /&gt;
&lt;br /&gt;
Sleep           16&lt;br /&gt;
        note    causes AlbertLink to sleep the remote link, waking up with a&lt;br /&gt;
                normal AlbertLink startup&lt;br /&gt;
&lt;br /&gt;
CheckSensors    17&lt;br /&gt;
        return  byte = sensor a id&lt;br /&gt;
                byte = sensor b id&lt;br /&gt;
                byte = sensor c id&lt;br /&gt;
                byte = sensor d id&lt;br /&gt;
&lt;br /&gt;
AskBack         18 +string&lt;br /&gt;
        note    string = input line&lt;br /&gt;
&lt;br /&gt;
ReadLabels      19&lt;br /&gt;
        return  [+string1 (+string2) .......]&lt;br /&gt;
        note    string1 = source label&lt;br /&gt;
        note    string2 = label (not sent if string1 is blank)&lt;br /&gt;
        note    string2 terminated by 0 for hard label and 128 for soft label&lt;br /&gt;
        note    end of list terminated by string1 being blank&lt;br /&gt;
&lt;br /&gt;
WriteLabel      20 +string1 +string2 +byte&lt;br /&gt;
        note    string1 = source label&lt;br /&gt;
        note    string2 = label&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        : 0, hard label&lt;br /&gt;
                        : 1, soft label&lt;br /&gt;
        return  byte&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        : 0, okay&lt;br /&gt;
                        : 1, bad source label&lt;br /&gt;
                        : 2, bad label&lt;br /&gt;
                        : 3, label too long&lt;br /&gt;
                        : 4, label exists as a procedure&lt;br /&gt;
                        : 5, can't overwrite hard label with a soft label&lt;br /&gt;
&lt;br /&gt;
FreeMem         21&lt;br /&gt;
        return  byte +byte2&lt;br /&gt;
        note    byte/2 = free memory&lt;br /&gt;
&lt;br /&gt;
TraceCont       22&lt;br /&gt;
        note    this causes procedure execution to continue from a stopped trace&lt;br /&gt;
&lt;br /&gt;
Clock           23 +byte&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        : 0, stop clock&lt;br /&gt;
                        : 1, start clock&lt;br /&gt;
                        : 2, reset clock&lt;br /&gt;
&lt;br /&gt;
PromptBack      24&lt;br /&gt;
        note    send this when the user has clicked on the &amp;quot;prompt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
FileBack        25 +byte&lt;br /&gt;
        note    byte = flag&lt;br /&gt;
                        : 0, cannot open file&lt;br /&gt;
                        : 1, file opened&lt;br /&gt;
        note    used in reply to File&lt;br /&gt;
&lt;br /&gt;
 Print, Trace, SteadyLine, Get and Error surround any tokens with 1 to start token and 2 to finish token. Put and SteadyLine will remove them automatically before processing.&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=258</id>
		<title>SmartBox OS</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_OS&amp;diff=258"/>
		<updated>2023-11-01T04:39:15Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Created page with &amp;quot;== Changelog ==   &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10) bob	072	CallOS 14 now checks current adc.owner status jim	072.d1	* Rewrite for new hardware/processor * jim	072.d1	Moved vectors/workspace to &amp;amp;440 jim	072.d1	Moved rs.inp.buf to &amp;amp;500 jim	072.d1	Moved jobout.buf to &amp;amp;600 jim	072.d1	Moved jobin.buf to &amp;amp;680 jim	072.d1	Moved jobs.status to &amp;amp;700 jim	072.d1	Added irq.X (&amp;amp;A4) jim	072.d1	Added irq.Y (&amp;amp;A5) jim	072.d1	Removed short detection code jim	072.d1	Voided CallO...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changelog ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;bob	071	Fixed MotorForward bug (%10)&lt;br /&gt;
bob	072	CallOS 14 now checks current adc.owner status&lt;br /&gt;
jim	072.d1	* Rewrite for new hardware/processor *&lt;br /&gt;
jim	072.d1	Moved vectors/workspace to &amp;amp;440&lt;br /&gt;
jim	072.d1	Moved rs.inp.buf to &amp;amp;500&lt;br /&gt;
jim	072.d1	Moved jobout.buf to &amp;amp;600&lt;br /&gt;
jim	072.d1	Moved jobin.buf to &amp;amp;680&lt;br /&gt;
jim	072.d1	Moved jobs.status to &amp;amp;700&lt;br /&gt;
jim	072.d1	Added irq.X (&amp;amp;A4)&lt;br /&gt;
jim	072.d1	Added irq.Y (&amp;amp;A5)&lt;br /&gt;
jim	072.d1	Removed short detection code&lt;br /&gt;
jim	072.d1	Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadADCReg,WriteADCReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadACIAReg,WriteACIAReg&lt;br /&gt;
jim	072.d1	Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow&lt;br /&gt;
jim	072.d1	Added CallOS 21 Write Outputs&lt;br /&gt;
jim	072.d1	Added CallOS 22 Read Outputs added&lt;br /&gt;
jim	072.d1	Added CallOS 23 Read Inputs added&lt;br /&gt;
jim	072.d1	Added CallOS 24 Write Motors&lt;br /&gt;
jim	072.d1	Added CallOS 25 Read Motors&lt;br /&gt;
jim	072.d1	Added CallOS 26 Read Keypad&lt;br /&gt;
jim	072.d1	Expanded CallOS 5 information&lt;br /&gt;
jim	072.d1	Added JobCall IdentSystem to return CallOS 5 info&lt;br /&gt;
jim	072.d1	JobCalls now use appropriate CallOS'&lt;br /&gt;
jim	072.d1	OS.READBYTE no longer preserves A on CS&lt;br /&gt;
jim	072.d1	irq.vec,irq2.vec,nmi.vec now redundant&lt;br /&gt;
jim	072.d1	Renamed JobCall DownloadData to DownloadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall UploadData to UploadData38&lt;br /&gt;
jim	072.d1	Renamed JobCall ExecuteCode to ExecuteCode38&lt;br /&gt;
jim	072.d1	Renamed JobCall ReadByte to ReadByte38&lt;br /&gt;
jim	072.d1	Renamed JobCall StoreByte to StoreByte38&lt;br /&gt;
jim	072.d1	JobCall ForcedADCRead now checks adc.owner first&lt;br /&gt;
jim	072.d3	Added CallOS 27 Write printer&lt;br /&gt;
jim	072.d3	Added CallOS 28 Read printer&lt;br /&gt;
jim	072.d3	Added CallOS 29 Write RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 30 Read RTC Reg&lt;br /&gt;
jim	072.d3	Added CallOS 31 Write RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 32 Read RTC string&lt;br /&gt;
jim	072.d3	Added CallOS 33 Write RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 34 Read RTC bcd&lt;br /&gt;
jim	072.d3	Added CallOS 35 Write LCD Reg&lt;br /&gt;
jim	072.d3	Added CallOS 36 Read LCD Reg&lt;br /&gt;
jim	072.d3	Added JobCall WritePrinter&lt;br /&gt;
jim	072.d3	Added JobCall ReadPrinter&lt;br /&gt;
jim	072.d3	Added JobCall PrintChar&lt;br /&gt;
jim	072.d3	Added JobCall PrintStreamZ&lt;br /&gt;
jim	072.d3	Added JobCall PrintStream&lt;br /&gt;
jim	072.d3	Added JobCall PrintServer&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCReg&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTC&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTC&lt;br /&gt;
jim	072.d3	Added JobCall WriteRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall ReadRTCbcd&lt;br /&gt;
jim	072.d3	Added JobCall WriteLCDReg&lt;br /&gt;
jim	072.d3	Added JobCall ReadLCDReg&lt;br /&gt;
jim	072.d3	Added CallOS 37 Write Power ctrl&lt;br /&gt;
jim	072.d3	Added CallOS 38 Read Power ctrl&lt;br /&gt;
jim	072.d3	Implemented OS.PRINTER&lt;br /&gt;
jim	072.d3	Added JobCall PatchMF (MotorForward)&lt;br /&gt;
jim	072.d3	Changed irq.vec to int0.vec&lt;br /&gt;
jim	072.d3	Changed irq2.vec to int1.vec&lt;br /&gt;
jim	072.d3	Added int2irq.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d3	Added int3irq.vec (wrksp+&amp;amp;22)&lt;br /&gt;
jim	072.d3	Added int4irq.vec (wrksp+&amp;amp;24)&lt;br /&gt;
jim	072.d3	Added c0irq.vec (wrksp+&amp;amp;26)&lt;br /&gt;
jim	072.d3	Added c1irq.vec (wrksp+&amp;amp;28)&lt;br /&gt;
jim	072.d3	Added t1irq.vec (wrksp+&amp;amp;2A)&lt;br /&gt;
jim	072.d3	Added t2irq.vec (wrksp+&amp;amp;2C)&lt;br /&gt;
jim	072.d3	Added txirq.vec (wrksp+&amp;amp;2E)&lt;br /&gt;
jim	072.d3	Added tyirq.vec (wrksp+&amp;amp;30)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;32)&lt;br /&gt;
jim	072.d3	Added s1rirq.vec (wrksp+&amp;amp;34)&lt;br /&gt;
jim	072.d3	Added s2irq.vec (wrksp+&amp;amp;36)&lt;br /&gt;
jim	072.d3	Added adcirq.vec (wrksp+&amp;amp;38)&lt;br /&gt;
jim	072.d3	Renamed JobCall DownloadData38 to DownloadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall UploadData38 to UploadData740&lt;br /&gt;
jim	072.d3	Renamed JobCall ExecuteCode38 to ExecuteCode740&lt;br /&gt;
jim	072.d3	Renamed JobCall ReadByte38 to ReadByte740&lt;br /&gt;
jim	072.d3	Renamed JobCall StoreByte38 to StoreByte740&lt;br /&gt;
jim	072.d4	Hardware update&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 37 to Write Power/Charge control&lt;br /&gt;
jim	072.d4	Re-assigned CallOS 38 to Read Power/Charge control&lt;br /&gt;
jim	072.d4	Moved int2irq.vec to wrksp+&amp;amp;22&lt;br /&gt;
jim	072.d4	Moved int3irq.vec to wrksp+&amp;amp;24&lt;br /&gt;
jim	072.d4	Moved int4irq.vec to wrksp+&amp;amp;26&lt;br /&gt;
jim	072.d4	Moved c0irq.vec to wrksp+&amp;amp;28&lt;br /&gt;
jim	072.d4	Moved c1irq.vec to wrksp+&amp;amp;2A&lt;br /&gt;
jim	072.d4	Moved t1irq.vec to wrksp+&amp;amp;2C&lt;br /&gt;
jim	072.d4	Moved t2irq.vec to wrksp+&amp;amp;2E&lt;br /&gt;
jim	072.d4	Moved txirq.vec to wrksp+&amp;amp;30&lt;br /&gt;
jim	072.d4	Moved tyirq.vec to wrksp+&amp;amp;32&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;34&lt;br /&gt;
jim	072.d4	Moved s1rirq.vec to wrksp+&amp;amp;36&lt;br /&gt;
jim	072.d4	Moved s2irq.vec to wrksp+&amp;amp;38&lt;br /&gt;
jim	072.d4	Moved adcirq.vec to wrksp+&amp;amp;3A&lt;br /&gt;
jim	072.d4	Added OS.LCDVDU (&amp;amp;FFB7) and lcdvdu.vec (wrksp+&amp;amp;20)&lt;br /&gt;
jim	072.d5	Re-assigned CallOS 37 to Write/Read Power/Charge control&lt;br /&gt;
jim	072.d5	Re-assgined CallOS 38 Read Battery Voltage&lt;br /&gt;
jim	072.d5	OS.CALLOS now re-entrant&lt;br /&gt;
jim	072.d5	Fixed OS.PRINTER&lt;br /&gt;
jim	072.d5	Started to add internal logging software&lt;br /&gt;
jim	072.d5	Moved OS.LCDVDU to &amp;amp;FFB6&lt;br /&gt;
jim	072.d7	Added OS.PRINTERPOLL (&amp;amp;FFB3) and printerpoll.vec (wrksp+&amp;amp;3C)&lt;br /&gt;
bob	073	Matched jim &amp;amp; bob sensor lookup tables&lt;br /&gt;
bob	073	Added PatchMF&lt;br /&gt;
jim	072.dj	Added OS.CALLOS 51 Read keypad press&lt;br /&gt;
jim	072.dj	Added hard reset keypad press&lt;br /&gt;
jim	072.dk	Added JobCall ReadOutputs&lt;br /&gt;
jim	072.do	Changed soft reset of zero page locations&lt;br /&gt;
jim	072.do	Changed MotorForward/Backward bitmaps&lt;br /&gt;
jim	072.dp	Fixed OS.CALLOS WriteRTCbcd&lt;br /&gt;
jim	072.dq	Fixed OS.CALLOS ReadRTCstring for correct 24hr operation&lt;br /&gt;
jim	072.dr	Changed reset prompts&lt;br /&gt;
bob	074	Added support for Little Bob&lt;br /&gt;
jim	072.dy	Added further RTC/CMOS support&lt;br /&gt;
jim	072.dB	Removed double hard reset&lt;br /&gt;
jim	072.dE	Set _cpu.mode depending on external memory requirement&lt;br /&gt;
jim	072.dF	Added Insight code&lt;br /&gt;
jim	073	Sub-release&lt;br /&gt;
jim	074	Modification to LCD code - uses bsy flag all the time now&lt;br /&gt;
jim	074	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	074	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added simple battery charge code&lt;br /&gt;
jim	075	Fixed JobCall ReadSensorTable&lt;br /&gt;
jim	075	Fixed OS.CALLOS 42 Write LCD Char Def&lt;br /&gt;
jim	075	Fixed OS.CALLOS 43 Read LCD Char Def&lt;br /&gt;
jim	075	Added sleep code&lt;br /&gt;
jim	075	Added OS.CALLOS 52 Write Sleep Time&lt;br /&gt;
jim	075	Added OS.CALLOS 53 Read Sleep Time&lt;br /&gt;
jim	075	Modified battery charge code&lt;br /&gt;
jim	076	Modified JobCall ReadSensorTable&lt;br /&gt;
jim	076	Modified OS.CALLOS 26 ReadKeypad to do debounce&lt;br /&gt;
jim	077	Rewrote battery charging code&lt;br /&gt;
jim	077.2	Reduced stop charge threshold to 2&lt;br /&gt;
jim	077.3	Increased stop charge threshold to 3&lt;br /&gt;
jim	077.3	Added battery voltage averaging&lt;br /&gt;
bill	077	Stripped&lt;br /&gt;
bill	077	Moved ins to out port&lt;br /&gt;
bill	077	Added RTS&lt;br /&gt;
bill	077	Changed sensor ID system back to original, using p2.1&lt;br /&gt;
bill	077	Dedicated USB/RS selection line, RTS provisioned on p2.3&lt;br /&gt;
bill	077	Overload &amp;quot;bounce&amp;quot; timing&lt;br /&gt;
bill	078	CTS added&lt;br /&gt;
bill	079	Temperature fudge&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Documentation ==&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;		Documentation for SmartBox OS 2.066&lt;br /&gt;
&lt;br /&gt;
	The term JobCall is the concept of the routine itself, as opposed to&lt;br /&gt;
JobName (which is the actual name associated with it (eg.  NameCode)) and&lt;br /&gt;
JobCode (which is the actual number associated with it (eg.  3)).&lt;br /&gt;
&lt;br /&gt;
	The term controller means the SmartBox, or any other incarnation&lt;br /&gt;
which may appear in future time !&lt;br /&gt;
&lt;br /&gt;
	The term OS means the controllers' Operating System.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Preview&lt;br /&gt;
&lt;br /&gt;
	There are a number of reasons why using Machine Code on the&lt;br /&gt;
controller is more desirable than using the various JobCalls via the serial&lt;br /&gt;
link.  One reason is speed, if the task which needs to be performed, has to&lt;br /&gt;
be performed very quickly (more quickly than can be achieved via the serial&lt;br /&gt;
link at least), then some code must be written to work in the controller&lt;br /&gt;
itself to achieve this sort of speed.  If the task required is to be a&lt;br /&gt;
&amp;quot;background&amp;quot; task (ie.  a task which may happen while other &amp;quot;things&amp;quot; carry&lt;br /&gt;
on as normal, like a change of state of a particular port) then it is far&lt;br /&gt;
easier to let the controller do all the work, maybe under interupts, than&lt;br /&gt;
continuously poll the controller checking the state of various things. &lt;br /&gt;
Another reason may be that you would like to add another JobCall to the OS,&lt;br /&gt;
extending the calls available to external applications (and the user at the&lt;br /&gt;
same time), without the user having to write the code himself to achieve the&lt;br /&gt;
aim.  For advanced users, there is also the ablity to alter the way the OS&lt;br /&gt;
does various things and to have the controller in their &amp;quot;power&amp;quot;, for&lt;br /&gt;
dedicated tasks etc.&lt;br /&gt;
&lt;br /&gt;
	One advantage of writing IN the controller is that that &amp;quot;function&amp;quot;&lt;br /&gt;
is available to any computer using the serial link, and is thus not fixed to&lt;br /&gt;
one computer type or even a computer at all, where the controller is used&lt;br /&gt;
free running, doing a particular task which doesn't need the intervention of&lt;br /&gt;
the user's own computer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Call types&lt;br /&gt;
&lt;br /&gt;
	There are three different types of machine code routines which can&lt;br /&gt;
be added to the controller.  Each one is suited to a more advanced level&lt;br /&gt;
than the other, which also relates to the amount of work needed to actually&lt;br /&gt;
get the routine working !  The three levels are such:&lt;br /&gt;
&lt;br /&gt;
	a) Simple stand alone routine.  Here the routine is just the routine&lt;br /&gt;
itself with no supporting code (ie.  code to integrate with the rest of the&lt;br /&gt;
OS).  It has to be called directly, by use of the ExecuteCode JobCall, thus&lt;br /&gt;
it usually resides at a fixed address and obviously does not offer the user&lt;br /&gt;
friendly way of accessing itself like there is with normal OS calls.&lt;br /&gt;
&lt;br /&gt;
	b) Extended JobCall.  There is one JobCall in the OS (ExtendJob)&lt;br /&gt;
which can call various routines residing in memory, each one is identified&lt;br /&gt;
by a 8 bit number.  The disadvantages of this way is that the call hasn't&lt;br /&gt;
got it's own JobName/JobCode and needs the extra byte to identify itself. &lt;br /&gt;
This is ideal for the quick routine which the user would like to add for&lt;br /&gt;
himself without having to resort to the extra code needed for the full&lt;br /&gt;
JobCode implementation.&lt;br /&gt;
&lt;br /&gt;
	c) Full JobCall.  This is where the routine can define itself one&lt;br /&gt;
(or a number) of JobNames/JobCodes, it can integrate itself into an&lt;br /&gt;
extension of the OS in a complete way.  The disadvantage is that extra&lt;br /&gt;
coding is needed to deal with the various OS calls it has to service and&lt;br /&gt;
that extra work is needed to produce a suitable file to place in the&lt;br /&gt;
controller.&lt;br /&gt;
&lt;br /&gt;
	Normally with most processors, including the 65c02, the machine code&lt;br /&gt;
produced is produced for a fixed memory address, and it cannot be made to&lt;br /&gt;
run in a different place in memory unless extra work is carried out by the&lt;br /&gt;
user, or at least a combination of the user and the OS.  For the controller&lt;br /&gt;
this is a very severe disadvantage as most extensions are downloaded as&lt;br /&gt;
&amp;quot;modules&amp;quot; and to optimise memory a system has to be devised to enable&lt;br /&gt;
routines to be placed anywhere in memory, thus allowing no memory to be&lt;br /&gt;
wasted.  In the OS there is a routine to do just this, though it does need&lt;br /&gt;
some help.  A special version of the users code has to be developed, or&lt;br /&gt;
rather it has to be &amp;quot;processed&amp;quot; to add extra information on the end.  Extra&lt;br /&gt;
code is needed to set up a few pointers and call the OS routine to relocate. &lt;br /&gt;
See later.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Memory&lt;br /&gt;
&lt;br /&gt;
	The first 256 bytes of memory are somewhat special, most zero page&lt;br /&gt;
locations are reserved for use by the OS:&lt;br /&gt;
&lt;br /&gt;
	0	General workspace, used to pass parameters to some OS&lt;br /&gt;
			routines, can also be used by other routines DURING&lt;br /&gt;
	14		their execution.&lt;br /&gt;
	15	Reserved&lt;br /&gt;
	6f&lt;br /&gt;
	70	Available to the user&lt;br /&gt;
	9f&lt;br /&gt;
	a0	Accumulator store for irqs (irq_A)&lt;br /&gt;
	a1	Two byte counter, decremented at 100hz, useful for temporary&lt;br /&gt;
	a2		timing purposes (fcount)&lt;br /&gt;
	a3	RAM size, high byte of RAM size of machine, ie. for 32k&lt;br /&gt;
			this will be &amp;amp;80 (RAM_size)&lt;br /&gt;
	a4	Reserved&lt;br /&gt;
	af&lt;br /&gt;
	b0	Used by the OS&lt;br /&gt;
	ff&lt;br /&gt;
&lt;br /&gt;
	The rest of memory space is used for the processors stack, OS'&lt;br /&gt;
buffers, tables, vectors and, of course, the user available memory.&lt;br /&gt;
&lt;br /&gt;
	A breakdown of the rest of memory:&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;100 to &amp;amp;1FF is the 65c02's stack.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;200-&amp;amp;2FF is used by the OS for various system functions and also&lt;br /&gt;
contains the vector table, more on that later.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;300-&amp;amp;3FF is the serial input buffer.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;400-&amp;amp;47F is the internal job code call output buffer. (jobout_buf)&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;480-&amp;amp;4FF is the internal job code call input buffer. (jobin_buf)&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;500-&amp;amp;5FF is the job status table.&lt;br /&gt;
&lt;br /&gt;
	&amp;amp;600 to the end of RAM memory is for the user. The user should not&lt;br /&gt;
		assume what the top of AVAILABLE memory is and use the&lt;br /&gt;
		system variable HIMEM.&lt;br /&gt;
&lt;br /&gt;
	In the controller the maximum limit of RAM is &amp;amp;8000 (which is 32k),&lt;br /&gt;
from &amp;amp;8000-&amp;amp;DFFF lies the memory mapped hardware, from &amp;amp;E000 to &amp;amp;FFF9 lies&lt;br /&gt;
the OS and from &amp;amp;FFFA to &amp;amp;FFFF lies the 65c02 vectors.&lt;br /&gt;
&lt;br /&gt;
	So the user has his own limits of free memory.  The lowest limit is&lt;br /&gt;
called LOMEM and the highest limit is called HIMEM, everything between these&lt;br /&gt;
boundaries is termed &amp;quot;available&amp;quot; for use by anyone, the user or the OS. &lt;br /&gt;
Anyone can also claim memory, thus making private to them and not allowing&lt;br /&gt;
anyone else to use it.  You do this by altering the value of LOMEM, HIMEM&lt;br /&gt;
should not be moved.  Of course anyone claiming memory should first check&lt;br /&gt;
that there is enough memory available for the amount they want to claim for. &lt;br /&gt;
To take a common example, a downloadable routine which installs itself as a&lt;br /&gt;
extension routine, The download routine will first of all read LOMEM, read&lt;br /&gt;
HIMEM, check there is enough memory for the routine, download it to LOMEM&lt;br /&gt;
and call it, the downloaded routine will then relocate itself and alter&lt;br /&gt;
LOMEM so it is protected.&lt;br /&gt;
&lt;br /&gt;
	There is one other memory limit, this is called TOPMEM, this is the&lt;br /&gt;
absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS&lt;br /&gt;
may alter HIMEM to below TOPMEM.  TOPMEM should be ignored ideally, and&lt;br /&gt;
HIMEM used.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS Calls&lt;br /&gt;
&lt;br /&gt;
	 The OS has calls (ie.  a jump instruction which you call, which&lt;br /&gt;
then calls, via a RAM vector, a routine in the OS) setup near the top of&lt;br /&gt;
memory, through these calls you do everything associated with the&lt;br /&gt;
controller.  The calls are are such:&lt;br /&gt;
&lt;br /&gt;
 OS_PRINTER	at address &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS	at address &amp;amp;FFBC&lt;br /&gt;
 OS_SENDBYTE	at address &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE	at address &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB	at address &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB	at address &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB	at address &amp;amp;FFCB&lt;br /&gt;
&lt;br /&gt;
	The address is the one to call for the particular routine.&lt;br /&gt;
&lt;br /&gt;
	OS_PRINTER is reserved for future use.&lt;br /&gt;
	OS_CALLOS calls upon the OS to do various things internally.&lt;br /&gt;
	OS_SENDBYTE sends a byte out of the serial port.&lt;br /&gt;
	OS_READBYTE reads a byte from the serial port.&lt;br /&gt;
	OS_SENDJOB 'sends' a value to the JobCall caller.&lt;br /&gt;
	OS_READJOB 'reads' a value from the JobCalls caller.&lt;br /&gt;
	OS_DECODEJOB decodes the Job number held in A and acts on it.&lt;br /&gt;
&lt;br /&gt;
	Note none of these things should be used in interupt routines,&lt;br /&gt;
except OS_CALLOS, of any sort, except if YOU know what state the machine is&lt;br /&gt;
in and WHY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Serial Port&lt;br /&gt;
&lt;br /&gt;
	The serial port on the controller is currently based on the 6850&lt;br /&gt;
UART, this need not bother you as the OS deals with the thing entirely by&lt;br /&gt;
itself.  The chip is setup by the OS to always have a word format of 8n1 and&lt;br /&gt;
a baud rate of 9600, this translates to 960cps.  The 6850 has two divide&lt;br /&gt;
rates for the baud rate, the OS only uses one, using the other one is beyond&lt;br /&gt;
the scope of this document and shouldn't be neccessary to use.&lt;br /&gt;
&lt;br /&gt;
	Only receive interupts are used, transmission of characters is done&lt;br /&gt;
on a polled basis.&lt;br /&gt;
&lt;br /&gt;
	As said, there are two calls OS_READBYTE and OS_SENDBYTE. &lt;br /&gt;
OS_READBYTE tests the serial input buffer and returns the character (if any&lt;br /&gt;
in the A register), the c flag states whether there was a character&lt;br /&gt;
returned.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_READBYTE&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	X, Y preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
		if c = 1 (no character received)&lt;br /&gt;
			A is preserved&lt;br /&gt;
		if c = 0 (character received)&lt;br /&gt;
			A = character received&lt;br /&gt;
&lt;br /&gt;
	OS_SENDBYTE sends the character in the A register out of the serial&lt;br /&gt;
port, taking into consideration flow control etc.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_SENDBYTE&lt;br /&gt;
 Entry	:	A = character to send&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y, c is preserved&lt;br /&gt;
		z = undefined&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS_READJOB and OS_SENDJOB&lt;br /&gt;
&lt;br /&gt;
	To send/receive data from the user (either internally or via the&lt;br /&gt;
serial port) you call OS_SENDJOB/OS_READJOB for the next byte.  If your&lt;br /&gt;
JobCall requires dynamic use of data, ie.  interaction, then a check of the&lt;br /&gt;
enviroment should be done to check the call came from the serial port.  One&lt;br /&gt;
example of this is the OS JobCall MultipleServer, which requires dynamic use&lt;br /&gt;
of sending a stream of data until a reaction from the user to tell it to&lt;br /&gt;
stop.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_READJOB&lt;br /&gt;
 Purpose: To read job value from the user&lt;br /&gt;
 Entry	:	A, X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = byte read&lt;br /&gt;
		X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Note   : When coming from the serial port it waits for a character to be&lt;br /&gt;
 received, if you are doing a serial only call and wish to have a loop&lt;br /&gt;
 checking for a character from the user while doing &amp;quot;your own thing&amp;quot;, then&lt;br /&gt;
 OS_READBYTE should be used.  For internal calls this reads data out of&lt;br /&gt;
 jobin_buf.&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_SENDJOB&lt;br /&gt;
 Purpose: To give a job value back to the user&lt;br /&gt;
 Entry	:	A = byte to give&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A, X, Y is preserved&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
  Note   : This will send either out to the serial port or place a byte in&lt;br /&gt;
 jobout_buf.  For internal calls this stores the value in jobout_buf, which&lt;br /&gt;
 is allocated 128 bytes, no bound checking is done so sending more than 128&lt;br /&gt;
 bytes to OS_SENDJOB will cause corruption of some memory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OS_DECODEJOB&lt;br /&gt;
&lt;br /&gt;
	 JobCalls in practise are really only used by the user, JobCalls&lt;br /&gt;
tend not to call other calls at all, infact you shouldn't really call&lt;br /&gt;
another call unless you were called from the serial port.  If you do need to&lt;br /&gt;
call another JobCall then you do it like this; with each call you have&lt;br /&gt;
associated with it a number, and the call might require some data or give&lt;br /&gt;
some data back or both.  With the serial port this is simple, you just send&lt;br /&gt;
data down and receive data as it is produced.  You can't do this with&lt;br /&gt;
internal calls, and some calls will not let you call them from inside the&lt;br /&gt;
controller, you HAVE to call them from outside.  But for the ones which do&lt;br /&gt;
let you, you do it with two data blocks, one for input TO the call and one&lt;br /&gt;
for output FROM the call.  These are setup in a position in memory&lt;br /&gt;
(jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this&lt;br /&gt;
should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound&lt;br /&gt;
checking).  So to start with you place in the input block the data you want&lt;br /&gt;
to give the call (if any), call it, and then act on the data in the output&lt;br /&gt;
block (if any).  The OS call OS_DECODEJOB is the one which actually calls&lt;br /&gt;
the JobCall handler and eventually the actual routine, you tell the JobCall&lt;br /&gt;
handler which call you want by placing the JobCode in the A register.&lt;br /&gt;
&lt;br /&gt;
So for OS_DECODEJOB:&lt;br /&gt;
&lt;br /&gt;
 Call	: OS_DECODEJOB&lt;br /&gt;
 Entry	:	A = JobCode&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf should contain any input data&lt;br /&gt;
 Exit	:	A = flag&lt;br /&gt;
		X = number of bytes given back by routine&lt;br /&gt;
		Y = undefined&lt;br /&gt;
		c = status, if set JobCall does not exist&lt;br /&gt;
		z = status, if set JobCall cannot be called internally&lt;br /&gt;
		jobout_buf contains any data sent from routine&lt;br /&gt;
&lt;br /&gt;
	A typical example would be:&lt;br /&gt;
&lt;br /&gt;
	LDA #readhimem		; JobCode for ReadHimem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
	LDX jobout_buf		; Read HIMEM from buffer LSB&lt;br /&gt;
	LDY jobout_buf+1	; Read HIMEM from buffer MSB&lt;br /&gt;
	STX jobin_buf		; Place LSB in input buffer&lt;br /&gt;
	STY jobin_buf+1		; Place MSB in input buffer&lt;br /&gt;
	LDA #writelomem		; JobCode for WriteLomem&lt;br /&gt;
	JSR OS_DECODEJOB	; Call the JobCall handler&lt;br /&gt;
&lt;br /&gt;
which would read HIMEM, and then write LOMEM to be the same value, leaving&lt;br /&gt;
no spare memory left.&lt;br /&gt;
&lt;br /&gt;
	The reason why you should not call an internal call from another&lt;br /&gt;
internal call is that the same buffers (jobin_buf and jobout_buf) would be&lt;br /&gt;
used for both calls, causing conflict between the two calls, or rather to&lt;br /&gt;
the originator of the primary call !  The only time it would be safe if is&lt;br /&gt;
if you have already got all the data you want from jobin_buf and haven't&lt;br /&gt;
placed any in the jobout_buf (it would be lost if you had done).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CallOS&lt;br /&gt;
&lt;br /&gt;
	This OS call does various calls which are completely linked to the&lt;br /&gt;
OS, everything from relocating code to reading and writing the hardware&lt;br /&gt;
ports.&lt;br /&gt;
&lt;br /&gt;
	As normal, a &amp;quot;function&amp;quot; request value is placed in the A register,&lt;br /&gt;
any extra data is put in the X and Y registers and a call to OS_CALLOS is&lt;br /&gt;
made, and data is passed back in X, Y, c and z.  The A register will return&lt;br /&gt;
0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A&lt;br /&gt;
is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do&lt;br /&gt;
not return 12, 14, 15 in A though), if the routine passes back the same&lt;br /&gt;
value in A (the exception being call 0) as you gave it then that OS_CALLOS&lt;br /&gt;
function is not supported.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 0&lt;br /&gt;
 Purpose: Returns the OS version number&lt;br /&gt;
 Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = Hardware release version&lt;br /&gt;
		XY = OS version number&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Hardware version 0 is obsolete&lt;br /&gt;
	  Hardware version 1 is SmartBox&lt;br /&gt;
	  	VIA at &amp;amp;8030&lt;br /&gt;
	  	UART at &amp;amp;8010&lt;br /&gt;
	  	ADC at &amp;amp;8000&lt;br /&gt;
	  	AUX_PORT (inputs) at &amp;amp;8020 &lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 1&lt;br /&gt;
 Purpose: To claim a block of JobCodes&lt;br /&gt;
 Entry	:	A = 1&lt;br /&gt;
		X = number of JobCodes required&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X = base JobCode, if 0, no big enough block available&lt;br /&gt;
		Y = Job ID number, 0 if no big enough block&lt;br /&gt;
		c = set if no big enough block&lt;br /&gt;
		z is preserved&lt;br /&gt;
 Note	: This call claims JobCodes codes from the JobCode list.  JobCodes&lt;br /&gt;
are in the range 0 to 255, 0 is special and is the JobCall &amp;quot;Blank&amp;quot;.  All the&lt;br /&gt;
OS ones are spaced out but an &amp;quot;application&amp;quot; can claim a block of JobCodes,&lt;br /&gt;
usually just the 1 call.  If there is not a contigous block big enough then&lt;br /&gt;
no JobCodes are allocated and the appropiate result is returned.  The Job ID&lt;br /&gt;
is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external&lt;br /&gt;
applications, each successful call increments the Job ID returned, meaning a&lt;br /&gt;
maximum of 255 successful calls (not important as their is not that many&lt;br /&gt;
free calls available !), the Job ID can be used to check whether a JobCall&lt;br /&gt;
request is yours.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 2&lt;br /&gt;
 Purpose: To handle NameCode request for the application.&lt;br /&gt;
 Entry	:	A = 2&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (terminated by CR)&lt;br /&gt;
		zero_gp3 = JobId (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (Name not found)&lt;br /&gt;
		if c = 0 (Name found)&lt;br /&gt;
			jobin_buf contains the JobCode&lt;br /&gt;
 Note	: Used via the internal_vec to convert from JobName to job code, the&lt;br /&gt;
table format is just a list of the JobNames (each one CR terminated)&lt;br /&gt;
terminated by a &amp;amp;ff&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 3&lt;br /&gt;
 Purpose: To handle CodeName request for the application.&lt;br /&gt;
 Entry	:	A = 3&lt;br /&gt;
		XY = block pointing to block of JobNames&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
		zero_gp3 = job id (as returned by CALLOS 1)&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		XY = undefined&lt;br /&gt;
		z is preserved&lt;br /&gt;
		if c = 1 (JobCode not found)&lt;br /&gt;
		if c = 0 (JobCode found)&lt;br /&gt;
			jobin contains JobName (terminated by CR)&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 4&lt;br /&gt;
 Purpose: To relocate code&lt;br /&gt;
 Entry	:	A = 4&lt;br /&gt;
		XY = execution address (offset from start of code)&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
		zero_gp1 = offset address of execution after relocation&lt;br /&gt;
		zero_gp2 = offset address of relocation BitMap&lt;br /&gt;
		zero_gp3 = length of code to relocate&lt;br /&gt;
 Exit	:	No exit, calls begining of code straight away&lt;br /&gt;
 Note	: See later section on Relocation&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 5&lt;br /&gt;
 Purpose: To &amp;quot;describe&amp;quot; the enviroment&lt;br /&gt;
 Entry	:	A = 5&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
		XY = address of table, format as such:&lt;br /&gt;
		1st byte: number of bytes (ie. 14 currently)&lt;br /&gt;
		byte 2+:&lt;br /&gt;
			address of VIA&lt;br /&gt;
			address of ACIA&lt;br /&gt;
			address of ADC&lt;br /&gt;
			address of AUX_PORT&lt;br /&gt;
			address of jobs_status&lt;br /&gt;
			address of jobin_buf&lt;br /&gt;
			address of jobout_buf&lt;br /&gt;
 Note	: It is preferred that this call is consulted first if direct use of&lt;br /&gt;
the hardware is going to be used to get the correct addresses, or use CALLOS&lt;br /&gt;
0 and decide what addresses/configuration to use from the Hardware version&lt;br /&gt;
number.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 6&lt;br /&gt;
 Purpose: To read a register in the VIA&lt;br /&gt;
 Entry	:	A = 6&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: The register number is not checked for range.  This call allows&lt;br /&gt;
access to the hardware without actually directly accessing the hardware&lt;br /&gt;
yourself.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 7&lt;br /&gt;
 Purpose: To write to a register in the VIA&lt;br /&gt;
 Entry	:	A = 7&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 8&lt;br /&gt;
 Purpose: To read a register in the ACIA&lt;br /&gt;
 Entry	:	A = 8&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 9&lt;br /&gt;
 Purpose: To write to a register in the ACIA&lt;br /&gt;
 Entry	:	A = 9&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 10&lt;br /&gt;
 Purpose: To read a register in the ADC&lt;br /&gt;
 Entry	:	A = 10&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 11&lt;br /&gt;
 Purpose: To write to a register in the ADC&lt;br /&gt;
 Entry	:	A = 11&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y = value to write&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		X, Y, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 12&lt;br /&gt;
 Purpose: To read a ADC channel&lt;br /&gt;
 Entry	:	A = 12&lt;br /&gt;
		X = channel number to read (0 to 3)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = reserved for future use&lt;br /&gt;
		if c = 0 (8 bit reading)&lt;br /&gt;
			Y = reading&lt;br /&gt;
			X = undefined&lt;br /&gt;
		if c = 1 (16 bit reading)&lt;br /&gt;
			X = reading (LSB)&lt;br /&gt;
			Y = reading (MSB)&lt;br /&gt;
		z = undefined&lt;br /&gt;
 Note	: The A register MAY not return 0, it is guaranteed though that it&lt;br /&gt;
will not return 12.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 13&lt;br /&gt;
 Purpose: To read a register in the AUX_PORT&lt;br /&gt;
 Entry	:	A = 13&lt;br /&gt;
		X = register (0 to 15)&lt;br /&gt;
		Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: See above&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 14 (OS 2.066+)&lt;br /&gt;
 Purpose: To start sensor type checking&lt;br /&gt;
 Entry	:	A = 14&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = 1&lt;br /&gt;
		Y = value read&lt;br /&gt;
		X, c, z is preserved&lt;br /&gt;
 Note	: A returns 1.  This flags the ADC routines to start checking the&lt;br /&gt;
sensors.  After calling this routine, you should poll CALLOS 15 to check&lt;br /&gt;
when all the sensors have been checked.&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 15 (OS 2.066+)&lt;br /&gt;
 Purpose: To check the status of sensor checking&lt;br /&gt;
 Entry	:	A = 15&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	A = status&lt;br /&gt;
		XY = pointer to sensor types block&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: A does not return 0, though it is guaranteed to not return 15.&lt;br /&gt;
	  Status of 0 = checking finished and no futher check pending&lt;br /&gt;
	  Status of 1 = Waiting for current ADC channel to finish converting&lt;br /&gt;
				before switching sensor checking on&lt;br /&gt;
	  Status of &amp;gt;127 = Checking sensor types&lt;br /&gt;
	  The block pointed to by XY consists of 4 bytes, each byte&lt;br /&gt;
	  	contains the sensor type for the appropiate sensor, a type&lt;br /&gt;
	  	of 0 means no sensor present&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 16 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the VIA&lt;br /&gt;
 Entry	:	A = 16&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 17 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the ACIA&lt;br /&gt;
 Entry	:	A = 17&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 18 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the OS' irq mask for the ADC&lt;br /&gt;
 Entry	:	A = 18&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 19 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the ACIA's ctrl register and OS' soft copy&lt;br /&gt;
 Entry	:	A = 19&lt;br /&gt;
		X = EOR mask&lt;br /&gt;
		Y = AND mask&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		X = old mask value&lt;br /&gt;
		Y = new mask value&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 CALLOS	: 20 (OS 2.069+)&lt;br /&gt;
 Purpose: To set the reset vector for battery back RAM support&lt;br /&gt;
 Entry	:	A = 20&lt;br /&gt;
                XY = address to set reset vector or 0 for read only&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	A = 0&lt;br /&gt;
 		XY = old reset vector address&lt;br /&gt;
		c, z is preserved&lt;br /&gt;
 Note	: Sets the reset vector to address supplied and sets check bytes in&lt;br /&gt;
workspace.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vectors&lt;br /&gt;
&lt;br /&gt;
	Some parts of the system are controlled by vectors, vectors also&lt;br /&gt;
exist to patch the normal operation of some parts of the system.  Vectors&lt;br /&gt;
are RAM pointers, which are called to point to the routine to use.  Being in&lt;br /&gt;
RAM this means that they can be altered by the user to allow the user to&lt;br /&gt;
modify the operation of part, or all, of a system call.  All the call&lt;br /&gt;
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. &lt;br /&gt;
There are vectors which the user patches to pick up unknown jobcodes, and to&lt;br /&gt;
service requests like NameCode/CodeName etc.&lt;br /&gt;
&lt;br /&gt;
	The vectors are placed at address &amp;amp;200 in memory, each are 2 bytes&lt;br /&gt;
long.&lt;br /&gt;
&lt;br /&gt;
 brk_vec	at address &amp;amp;200&lt;br /&gt;
 nmi_vec	at address &amp;amp;202&lt;br /&gt;
 irq_vec	at address &amp;amp;204&lt;br /&gt;
 irq2_vec	at address &amp;amp;206&lt;br /&gt;
 sendserial_vec	at address &amp;amp;208&lt;br /&gt;
 readserial_vec	at address &amp;amp;20A&lt;br /&gt;
 sendjob_vec	at address &amp;amp;20C&lt;br /&gt;
 readjob_vec	at address &amp;amp;20E&lt;br /&gt;
 decode_job_vec	at address &amp;amp;210&lt;br /&gt;
 unknownjob_vec	at address &amp;amp;212&lt;br /&gt;
 extjob_vec	at address &amp;amp;214&lt;br /&gt;
 centisec_vec	at address &amp;amp;216&lt;br /&gt;
 internal_vec	at address &amp;amp;218&lt;br /&gt;
 callos_vec	at address &amp;amp;21A&lt;br /&gt;
 printer_vec	at address &amp;amp;21C&lt;br /&gt;
 reset_vec	at address &amp;amp;21E&lt;br /&gt;
&lt;br /&gt;
	The brk_vec is called whenever there is a BRK error, which should&lt;br /&gt;
not occur under normal use, as the controller OS does not make use of BRK&lt;br /&gt;
errors.&lt;br /&gt;
	The nmi_vec is called whenever there is a NMI request (not really&lt;br /&gt;
applicable, as the NMI line is not connected to anything).&lt;br /&gt;
	The irq_vec is called whenever there is an interupt.&lt;br /&gt;
	The irq2_vec is called whenever an unknown interupt is encountered,&lt;br /&gt;
ie.  didnXt come from the controllerXs VIA, ACIA or ADC, or the interupt&lt;br /&gt;
from the VIA was not used by the OS.&lt;br /&gt;
	The sendserial_vec is called when OS_SENDBYTE is called.&lt;br /&gt;
	The readserial_vec is called when OS_READBYTE is called.&lt;br /&gt;
	The sendjob_vec is called when OS_SENDJOB is called.&lt;br /&gt;
	The readjob_vec is called when OS_READJOB is called.&lt;br /&gt;
	The decodejob_vec is called when OS_DECODEJOB is called.  Note that&lt;br /&gt;
OS_DECODEJOB first calls a OS routine which sets a flag to say it's been&lt;br /&gt;
called internally, JobCode requests the OS gets from the serial port are&lt;br /&gt;
called via this vector, with a flag saying &amp;quot;from the serial port&amp;quot;.&lt;br /&gt;
	The unknownjob_vec is called whenever an unknown JobCode is&lt;br /&gt;
encountered.&lt;br /&gt;
	The extendedjob_vec is called whenever the ExtendedJob is called,&lt;br /&gt;
the A register holds the extension value.&lt;br /&gt;
	The centisecond_vec is called 100 times a second from the IRQ&lt;br /&gt;
routine from interupts off timer 1 of the VIA.  The OS has its pulsing&lt;br /&gt;
routines on the end of this.  Note that you should return with a RTS, not&lt;br /&gt;
RTI, you may also corrupt any of the registers.&lt;br /&gt;
	The internal_vec is called when some information is needed from&lt;br /&gt;
various parts of the system, which includes the OS whch lies on the end of&lt;br /&gt;
this vector.  An example is NameCode, which calls this vector to ask&lt;br /&gt;
everybody if they recognise the JobName in question.&lt;br /&gt;
	The callos_vec is called when OS_CALLOS is called.&lt;br /&gt;
	The printer_vec is called when OS_PRINTER is called.&lt;br /&gt;
	The reset_vec is called when the OS gets a reset and the internal&lt;br /&gt;
check bytes flag the integrity of the RAM.  It is first called with C&lt;br /&gt;
cleared for everything to setup vectors and then called with C set for a&lt;br /&gt;
foreground &amp;quot;language&amp;quot; application to start up.  Use CALLOS 20 to set this&lt;br /&gt;
vector.&lt;br /&gt;
	All vectors should be 'daisy chained', ie.  any routine which uses&lt;br /&gt;
any of them should store the current value in its own workspace and alter&lt;br /&gt;
the vector and at the end of the routine which services the vector should&lt;br /&gt;
jump to the original saved value, if this isnXt done the system may 'fall'&lt;br /&gt;
down.&lt;br /&gt;
	unknown, extendjob and irq2 all point to (in the present OS) to a&lt;br /&gt;
routine which just returns.  brk in the current OS just resets the stack and&lt;br /&gt;
jumps back to the OS' main idle loop.&lt;br /&gt;
	Irqs are reenabled before brk_vec is called&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Irq_vec&lt;br /&gt;
&lt;br /&gt;
	This is called when the 65c02 receives an irq from a device.  As the&lt;br /&gt;
65c02 signals brk and irq through the same cpu vector the OS first finds out&lt;br /&gt;
which one it really was and calls either brk_vec or irq_vec.  It also stores&lt;br /&gt;
the A reg at address &amp;amp;a0, as the A reg is corrupted while it finds out which&lt;br /&gt;
vector to really call, any irq routine which has claimed the irq should&lt;br /&gt;
restore the A reg from &amp;amp;a0 before returning via a RTI, else you should pass&lt;br /&gt;
on the call to the old vector owner.&lt;br /&gt;
&lt;br /&gt;
	irq2_vec is called by the OS irq routine if it does not find a irq&lt;br /&gt;
it can service (or as been masked out) in as-good-as the same conditions as&lt;br /&gt;
irq_vec is called.  The OS has a default irq2_vec handler of restoring A&lt;br /&gt;
from &amp;amp;a0 and doing a RTI.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Internal_vec and Unknownjob_vec&lt;br /&gt;
&lt;br /&gt;
	The main operation of extended JobCalls rely on patching some&lt;br /&gt;
vectors, two infact.  These two are unknownjob_vec and internal_vec. &lt;br /&gt;
unknownjob_vec is the one which the OS calls to actually tell everybody that&lt;br /&gt;
a JobCall has been requested which it does not know about (the JobCall MUST&lt;br /&gt;
have been claimed before hand with CALLOS, function 1, or else it is not&lt;br /&gt;
passed on).  internal_vec is the one which the OS calls in request to a&lt;br /&gt;
NameCode or CodeName.&lt;br /&gt;
&lt;br /&gt;
	The vectors are defined as thus:&lt;br /&gt;
&lt;br /&gt;
 internal_vec&lt;br /&gt;
&lt;br /&gt;
 Function: 0&lt;br /&gt;
 Purpose:	Nothing&lt;br /&gt;
 Entry	:	A = 0&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Exit	:	Leave everything as it is and exit straight away&lt;br /&gt;
 Note	: Used for claimed calls&lt;br /&gt;
&lt;br /&gt;
 Function: 1&lt;br /&gt;
 Purpose:	NameCode request&lt;br /&gt;
 Entry	:	A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobName (CR terminated)&lt;br /&gt;
 Exit	:	 if JobName recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			first byte of jobin_buf contains matched JobCode&lt;br /&gt;
		if JobName not recognised then&lt;br /&gt;
			A = 1&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
 Function: 2&lt;br /&gt;
 Purpose:	CodeName request&lt;br /&gt;
 Entry	:	A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
		jobin_buf contains JobCode&lt;br /&gt;
 Exit	:	if JobCode recognised then&lt;br /&gt;
			A = 0&lt;br /&gt;
			jobin_buf contains matched JobName (CR terminated)&lt;br /&gt;
		if Code not recognised then&lt;br /&gt;
			A = 2&lt;br /&gt;
		X, Y, c, z = undefined&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
	All other calls (ie.  not 0, 1 or 2 should be passed on straight&lt;br /&gt;
away for future expansion)&lt;br /&gt;
&lt;br /&gt;
 unknownjob_vec&lt;br /&gt;
&lt;br /&gt;
 Purpose:	To pass on unknown JobCalls to the correct owner&lt;br /&gt;
 Entry	:	A = enviroment (0 = internal call, 1 = serial call)&lt;br /&gt;
		Y = JobCode&lt;br /&gt;
		X = JOB ID of owner&lt;br /&gt;
		c, z = undefined&lt;br /&gt;
 Exit	:	if jobcode yours then&lt;br /&gt;
		if wrong enviroment then&lt;br /&gt;
			A = 1&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if right enviroment then&lt;br /&gt;
			perform function&lt;br /&gt;
			A = 0&lt;br /&gt;
			Y = 0&lt;br /&gt;
			X, c, z = undefined&lt;br /&gt;
		if jobcode not yours then&lt;br /&gt;
			pass on with registers unaltered&lt;br /&gt;
 Note	:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Starting Up&lt;br /&gt;
&lt;br /&gt;
	The setup procedure for setting up extension job calls is to:&lt;br /&gt;
&lt;br /&gt;
	a) Claim the number of JobCodes you want&lt;br /&gt;
		if success:&lt;br /&gt;
	b) patch the internal_vec and unknownjob_vec vectors&lt;br /&gt;
	c) move LOMEM up to protect your program&lt;br /&gt;
	d) do anything else for initial startup you may want to do&lt;br /&gt;
	e) return back, ready and waiting&lt;br /&gt;
&lt;br /&gt;
  This in code looks like:&lt;br /&gt;
&lt;br /&gt;
execute&lt;br /&gt;
	LDX #no_of_calls	; Number of JobCodes wanted&lt;br /&gt;
	LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	CPX #0			; Check we were given some codes&lt;br /&gt;
	BEQ noinstall		; No, don�t bother installing ourself&lt;br /&gt;
	STX call		; Store base address of JobCodes allocated&lt;br /&gt;
	STY job_id		; Store JOB ID given&lt;br /&gt;
&lt;br /&gt;
	LDA internal_vec	; Get old internal_vec (LSB)&lt;br /&gt;
	STA ovec		; Store it&lt;br /&gt;
	LDA internal_vec+1	; Get old internal_vec (MSB)&lt;br /&gt;
	STA ovec+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;internal_handle	; Our internal handler (LSB)&lt;br /&gt;
	STA internal_vec 	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;internal_handle	; Our internal handler (MSB)&lt;br /&gt;
	STA internal_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)&lt;br /&gt;
	STA ovec2 		; Store it&lt;br /&gt;
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)&lt;br /&gt;
	STA ovec2+1		; Store it&lt;br /&gt;
	LDA #&amp;gt;job_handle	; Our unknown job handler (LSB)&lt;br /&gt;
	STA unknownjob_vec	; Place it in the vector (LSB)&lt;br /&gt;
	LDA #&amp;lt;job_handle	; Our unknown job handler (MSB)&lt;br /&gt;
	STA unknownjob_vec+1	; Place it in the vector (MSB)&lt;br /&gt;
&lt;br /&gt;
	LDX #&amp;gt;WriteLomem	; Point to JobName&lt;br /&gt;
	LDY #&amp;lt;WriteLomem&lt;br /&gt;
	JSR findjob		; Find WriteLomem code&lt;br /&gt;
	BEQ noinstall		; Doesn't exist !&lt;br /&gt;
	LDX #&amp;gt;end_of_code	; End of code (LSB)&lt;br /&gt;
	LDY #&amp;lt;end_of_code	; End of code (MSB)&lt;br /&gt;
	STX jobin_buf		; Place it in jobin_buf (LSB)&lt;br /&gt;
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
	JSR OS_DECODEJOB	; Call JobCall handler&lt;br /&gt;
&lt;br /&gt;
noinstall&lt;br /&gt;
	RTS			; End - return to OS&lt;br /&gt;
&lt;br /&gt;
findjob&lt;br /&gt;
	STX zero.gp1		; Zero page&lt;br /&gt;
	STY zero.gp1+&lt;br /&gt;
	LDY #0&lt;br /&gt;
findthisjoblo&lt;br /&gt;
	LDA (zero.gp1),Y&lt;br /&gt;
	STA jobin.buf,Y		; Store name in jobin_buf&lt;br /&gt;
	INY&lt;br /&gt;
	CMP #13&lt;br /&gt;
	BNE findthisjoblo&lt;br /&gt;
	LDA #3			; NameCode&lt;br /&gt;
	JSR OS.DECODEJOB	; Call NameCode&lt;br /&gt;
	LDA jobout.buf		; Get returned value&lt;br /&gt;
	RTS&lt;br /&gt;
&lt;br /&gt;
WriteLomem	STR &amp;quot;WriteLomem&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	This will setup the various vectors and memory pointers for the&lt;br /&gt;
applicaion, the actual routines (internal_handle and job_handle) are fairly&lt;br /&gt;
straight forward and simple:&lt;br /&gt;
&lt;br /&gt;
internal_handle&lt;br /&gt;
	CMP #1			: Is it function call 1 (NameCode) ?&lt;br /&gt;
	BNE internal_handle_2	; No, check for function 2&lt;br /&gt;
	LDA job_id		; Get our JOB ID number&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #2			; CALLOS function 2&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	: Found, go off and claim call&lt;br /&gt;
	LDA #1			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_handle_2&lt;br /&gt;
	CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
	BNE internal_handle_3	; No, unknown, pass on&lt;br /&gt;
	LDA job_id		; Get our JOB ID&lt;br /&gt;
	STA zero_gp3		; Place it in zero_gp3 for CALLOS&lt;br /&gt;
	LDX #&amp;gt;job_names		; Our Job name table (LSB)&lt;br /&gt;
	LDY #&amp;lt;job_names		; Our Job name table (MSB)&lt;br /&gt;
	LDA #3			; CALLOS function 3&lt;br /&gt;
	JSR OS_CALLOS		; Call CALLOS&lt;br /&gt;
	BCC internal_yes	; Found, go off and claim call&lt;br /&gt;
	LDA #2			; Not found, restore A&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
internal_yes&lt;br /&gt;
	LDA #0			; We want to claim call for some reason&lt;br /&gt;
&lt;br /&gt;
internal_handle_3&lt;br /&gt;
	JMP (ovec)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle&lt;br /&gt;
	PHA			; Mark sure to preserve A&lt;br /&gt;
	CPY call		; Check call wanted against base of ours&lt;br /&gt;
	BCC job_handle_no	; Less than our base, so definately not ours&lt;br /&gt;
	TYA&lt;br /&gt;
	SBC #no_of_calls	; Subtract number of calls we have&lt;br /&gt;
	CMP call		; Now compare with base&lt;br /&gt;
	BCC job_handle2		; Yes, one of ours&lt;br /&gt;
&lt;br /&gt;
job_handle.no&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (ovec2)		; Call back to old vector&lt;br /&gt;
&lt;br /&gt;
job_handle2&lt;br /&gt;
	TYA&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC call		; Subtract our base from call&lt;br /&gt;
	ASL A			; Times by 2 for offset into table&lt;br /&gt;
	TAY			; Place in Y&lt;br /&gt;
	LDA job_run_table,Y	; Get LSB of routine&lt;br /&gt;
	STA zero_gp1		; Store it for indirect jump (LSB)&lt;br /&gt;
	LDA job_run_table+1,Y	; Get MSB of routine&lt;br /&gt;
	STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
	PLA			; Restore A&lt;br /&gt;
	JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
&lt;br /&gt;
job_names			; List of our JobNames&lt;br /&gt;
	STR &amp;quot;TestJob&amp;quot;		; Test job name&lt;br /&gt;
	DFB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
&lt;br /&gt;
job_run_table			; List of routine addresses to match Jobs&lt;br /&gt;
	DFW job_TestJob		; Test job routine&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relocation&lt;br /&gt;
&lt;br /&gt;
	The relocation method used will not do a complete relocation, ie. &lt;br /&gt;
from ANY address to ANY address.  The only constraint on the addresses is&lt;br /&gt;
that they must be the same offset in the page, which means, &amp;amp;2602 and &amp;amp;3402&lt;br /&gt;
is alright but &amp;amp;2602 and &amp;amp;3455 is not, ie.  the difference between the two&lt;br /&gt;
addresses must be a multiple of 256 bytes, as only the MSB is altered, this&lt;br /&gt;
should not cause any real problems.  What is meant by the two addresses is&lt;br /&gt;
the address the code was originally assembled at and the address it wants&lt;br /&gt;
relocating to.  Because of this programs must be multiples of 256 bytes, ie. &lt;br /&gt;
the low byte of LOMEM must ALWAYS be 0, your program should be padded out.&lt;br /&gt;
&lt;br /&gt;
	The relocation mechanism works by having a table, and to each byte&lt;br /&gt;
of code there is one BIT, if unset it means the byte of code should not be&lt;br /&gt;
relocated, else it should be, thus the BitMap table is 8 times smaller than&lt;br /&gt;
the code.  To generate the BitMap you have to assemble the code at two&lt;br /&gt;
different places, and compare both sets of assembled code against each&lt;br /&gt;
other, the differences are where the code is to be relocated.  The code&lt;br /&gt;
which is sent up to the controller is assumed to be assembled at address&lt;br /&gt;
&amp;amp;100, so the first code should be assembled at &amp;amp;100, the other one should be&lt;br /&gt;
assembled at another address, say &amp;amp;600, and the first one be sent up with&lt;br /&gt;
the BitMap.  A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with&lt;br /&gt;
BBC BASIC to compare the two files and create the BitMap.&lt;br /&gt;
&lt;br /&gt;
	All the program has to do to relocate itself is execute this bit of&lt;br /&gt;
code first of all: (address is the base address of the program, ie.  &amp;amp;100&lt;br /&gt;
for the downloaded version)&lt;br /&gt;
&lt;br /&gt;
execcall&lt;br /&gt;
	LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
	STA zero_gp1&lt;br /&gt;
	LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
	STA zero_gp1+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
	STA zero_gp2&lt;br /&gt;
	LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
	STA zero_gp2+1&lt;br /&gt;
&lt;br /&gt;
	LDA #&amp;gt;(execcall-address); Length of data to relocate (LSB)&lt;br /&gt;
	STA zero_gp3&lt;br /&gt;
	LDA #&amp;lt;(execcall-address); Length of data to relocate (MSB)&lt;br /&gt;
	STA zero_gp3+1&lt;br /&gt;
&lt;br /&gt;
	TXA			; Execcall REAL address&lt;br /&gt;
	SEC&lt;br /&gt;
	SBC #&amp;gt;(execcall-address); Subrtract length&lt;br /&gt;
	TAX			; Put it back in X&lt;br /&gt;
	TYA			; Ditto for MSB&lt;br /&gt;
	SBC #&amp;lt;(execcall-address)&lt;br /&gt;
	TAY&lt;br /&gt;
	LDA #4			; CALLOS function for Relocate&lt;br /&gt;
	JMP OS_CALLOS		; Call CALLOS&lt;br /&gt;
&lt;br /&gt;
BitMap				; BitMap start&lt;br /&gt;
&lt;br /&gt;
	This will relocate the code and automatically call the execution&lt;br /&gt;
address, which should be the initialisation code (ie.  setup internal_vec&lt;br /&gt;
and unknownjob_vec etc.).  &amp;quot;address&amp;quot; is the address the code is started to&lt;br /&gt;
assemble at, &amp;amp;100 for the version to be uploaded.  The upload routine should&lt;br /&gt;
pass in X and Y (via ExecuteCode) the address of the start of execcall, this&lt;br /&gt;
means that the routine can tell where it is in memory, subtracting the&lt;br /&gt;
length gives the start of the code to relocate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Layout Summary&lt;br /&gt;
&lt;br /&gt;
	The layout of a full application should be like this by now:&lt;br /&gt;
&lt;br /&gt;
	start of code&lt;br /&gt;
		program job codes, data etc.&lt;br /&gt;
		internal handler&lt;br /&gt;
		job handler&lt;br /&gt;
	end of code&lt;br /&gt;
		initialisation (setup internal etc.)&lt;br /&gt;
	end of code to be relocated&lt;br /&gt;
		execution (relocation)&lt;br /&gt;
		relocation BitMap&lt;br /&gt;
&lt;br /&gt;
	Because of memory space the initialisation code should be placed&lt;br /&gt;
outside the reserved memory limit of the program, as it is only called once&lt;br /&gt;
and not needed again.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Other miscellaneous calls:&lt;br /&gt;
&lt;br /&gt;
Call		: OS_PRINTER&lt;br /&gt;
Purpose	: To place a character in the printer buffer&lt;br /&gt;
Entry		: A = character to send&lt;br /&gt;
		  X, Y, c, z = undefined&lt;br /&gt;
Exit		: A, X, Y is preserved&lt;br /&gt;
		  if c = 0 (printed)&lt;br /&gt;
			The printer is on and the character was inserted&lt;br /&gt;
		  if c = 1 (not printed)&lt;br /&gt;
			The printer is off and the character was forgotten&lt;br /&gt;
		  z = undefined&lt;br /&gt;
Note		: The character goes into the printer buffer and will be&lt;br /&gt;
actually sent to the printer when the printer asks for it. If the printer&lt;br /&gt;
was not awake to begin with, an attempt is made to wake it up, thus&lt;br /&gt;
meaning that in some circumstances when the printer wasn't ready a&lt;br /&gt;
character may be lost, this cannot be helped. If the printer is off (via&lt;br /&gt;
the BCD switch) then the character will be forgotten as the Printer Buffer&lt;br /&gt;
is inactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  The module is located at &amp;amp;8000 in memory, and should be a EPROM placed&lt;br /&gt;
in the second ROM socket in the controller (ie. the top RAM socket). This&lt;br /&gt;
module is primary designed to turn the Controller into a dedicated task,&lt;br /&gt;
making it perform some function from switch on. It could also be used to&lt;br /&gt;
add more permant JobCalls to the Controller, in the process taking up much&lt;br /&gt;
less RAM), but because of there being only one Module present this feature&lt;br /&gt;
is limited, and is much more suited to a collection of the user's own&lt;br /&gt;
personal routines.&lt;br /&gt;
  For the OS to recognise a module as being in place the text &amp;quot;Module&amp;quot; is&lt;br /&gt;
looked for, the layout of a module should be like this (starting at&lt;br /&gt;
&amp;amp;8000):&lt;br /&gt;
&lt;br /&gt;
Offset		value			comment&lt;br /&gt;
 0		&amp;quot;Module&amp;quot;		The OS checks for this string&lt;br /&gt;
 6		0			End of check string&lt;br /&gt;
 7		Language entry		Address of 'Language' entry&lt;br /&gt;
 9		Service entry		Address of 'Service' entry&lt;br /&gt;
11		&amp;quot;&amp;lt;Module Title&amp;gt;&amp;quot; 	Module title&lt;br /&gt;
		0			End of Module title&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;&amp;lt;Module part&amp;gt;&amp;quot;		Part Module Title&lt;br /&gt;
		0			End of Part Module Title&lt;br /&gt;
		&amp;quot;x.xx&amp;quot;			Part version number&lt;br /&gt;
&lt;br /&gt;
		&amp;amp;FF			End of parts list&lt;br /&gt;
&lt;br /&gt;
  The language entry is a 2 byte value holding the address of the language&lt;br /&gt;
entry routine, this is called on reset, if the BCD is set correctly. A&lt;br /&gt;
value of less than &amp;amp;8000 means there is no valid language entry and any&lt;br /&gt;
language calls are ignored. If a RTS is made the OS carries on as it would&lt;br /&gt;
have if there wasn't a language call.&lt;br /&gt;
&lt;br /&gt;
  The service entry is a 2 byte value holding the address of the service&lt;br /&gt;
entry routine, this is called at various appropiate moments with service&lt;br /&gt;
numbers in A. A list of current calls is thus:&lt;br /&gt;
&lt;br /&gt;
	  0	- Not used&lt;br /&gt;
	  1	- Unknown Job Code&lt;br /&gt;
	  2	- Centisecond call&lt;br /&gt;
	  3	- Irq2 (unknown IRQ)&lt;br /&gt;
	  4	- internal_vec call (exit with A=0 to claim)&lt;br /&gt;
	254	- RESET&lt;br /&gt;
	255	- BRK&lt;br /&gt;
&lt;br /&gt;
  On exit all registers and flags should be preserved. For call 1 the&lt;br /&gt;
unknown job value is held in the Y register, the enviroment is held in the&lt;br /&gt;
X register, on exit, X should contain the flag, as A does from the exit of&lt;br /&gt;
unknownjob_vec. For the rest of calls, X and Y are undefined.&lt;br /&gt;
&lt;br /&gt;
  For both Service and Language entry points, a value of 0 means &amp;quot;there is&lt;br /&gt;
no valid routine&amp;quot; and the module isn't called for that type of entry.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Finally a full implementation of a JobCall, from start to finish.&lt;br /&gt;
&lt;br /&gt;
 DIM data% &amp;amp;1000&lt;br /&gt;
 :&lt;br /&gt;
 no_of_calls = 1&lt;br /&gt;
 :&lt;br /&gt;
 VIA = &amp;amp;E030&lt;br /&gt;
 ACIA = &amp;amp;E010&lt;br /&gt;
 ADC = &amp;amp;E000&lt;br /&gt;
 AUX_PORT = &amp;amp;E020&lt;br /&gt;
 brk_vec = &amp;amp;200&lt;br /&gt;
 nmi_vec = &amp;amp;202&lt;br /&gt;
 irq_vec = &amp;amp;204&lt;br /&gt;
 irq2_vec = &amp;amp;206&lt;br /&gt;
 sendserial_vec = &amp;amp;208&lt;br /&gt;
 readserial_vec = &amp;amp;20A&lt;br /&gt;
 sendjob_vec = &amp;amp;20C&lt;br /&gt;
 readjob_vec = &amp;amp;20E&lt;br /&gt;
 decodejob_vec = &amp;amp;210&lt;br /&gt;
 unknownjob_vec = &amp;amp;212&lt;br /&gt;
 extjob_vec = &amp;amp;214&lt;br /&gt;
 centisec_vec = &amp;amp;216&lt;br /&gt;
 internal_vec = &amp;amp;218&lt;br /&gt;
 callos_vec = &amp;amp;21A&lt;br /&gt;
 printer_vec = &amp;amp;21C&lt;br /&gt;
 zero_gp1 = 0&lt;br /&gt;
 zero_gp2 = 2&lt;br /&gt;
 zero_gp3 = 4&lt;br /&gt;
 zero_gp4 = 6&lt;br /&gt;
 zero_gp5 = 8&lt;br /&gt;
 zero_gp6 = 10&lt;br /&gt;
 zero_gp7 = 12&lt;br /&gt;
 zero_gp8 = 14&lt;br /&gt;
 zero_gp9 = 16&lt;br /&gt;
 zero_gp10 = 18&lt;br /&gt;
 user_reserved = &amp;amp;70&lt;br /&gt;
 irq_A = &amp;amp;A0&lt;br /&gt;
 fcount = &amp;amp;A1&lt;br /&gt;
 RAM_size = &amp;amp;A3&lt;br /&gt;
 jobout_buf = &amp;amp;400&lt;br /&gt;
 jobin_buf = &amp;amp;480&lt;br /&gt;
 OS_PRINTER = &amp;amp;FFB9&lt;br /&gt;
 OS_CALLOS = &amp;amp;FFBC	&lt;br /&gt;
 OS_SENDBYTE = &amp;amp;FFBF&lt;br /&gt;
 OS_READBYTE = &amp;amp;FFC2&lt;br /&gt;
 OS_SENDJOB = &amp;amp;FFC5&lt;br /&gt;
 OS_READJOB = &amp;amp;FFC8&lt;br /&gt;
 OS_DECODEJOB = &amp;amp;FFCB&lt;br /&gt;
 :&lt;br /&gt;
 FOR create=1 TO 2&lt;br /&gt;
 :&lt;br /&gt;
 FOR pass%=4 TO 7 STEP 3&lt;br /&gt;
 P%=&amp;amp;100*create : O%=data%&lt;br /&gt;
 [OPT pass%&lt;br /&gt;
 :&lt;br /&gt;
 .job_DemoJob&lt;br /&gt;
 CMP #1&lt;br /&gt;
 BEQ DemoJob_go&lt;br /&gt;
 LDA #1&lt;br /&gt;
 LDY #0&lt;br /&gt;
 RTS&lt;br /&gt;
 /&lt;br /&gt;
 .DemoJob_go&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 :&lt;br /&gt;
 .internal_handle&lt;br /&gt;
 CMP #1			; Is it function call 1 (NameCode) ?&lt;br /&gt;
 BNE internal_handle_2		; No, check for function 2&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID number&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #2			; CALLOS function 2&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #1			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_2&lt;br /&gt;
 CMP #2			; Is it function call 2 (CodeName) ?&lt;br /&gt;
 BNE internal_handle_3		; No, unknown, pass on&lt;br /&gt;
 /&lt;br /&gt;
 LDA job_id			; Get our JOB ID&lt;br /&gt;
 STA zero_gp3			; Place it in zero_gp3 for CALLOS&lt;br /&gt;
 LDX #job_names MOD 256	; Our Job name table (LSB)&lt;br /&gt;
 LDY #job_names DIV 256	; Our Job name table (MSB)&lt;br /&gt;
 LDA #3			; CALLOS function 3&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 BCC internal_yes		; Found, go off and claim call&lt;br /&gt;
 LDA #2			; Not found, restore A&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .internal_yes&lt;br /&gt;
 LDA #0			; We want to claim call for some reason&lt;br /&gt;
 /&lt;br /&gt;
 .internal_handle_3&lt;br /&gt;
 JMP (ovec)			; Call back to old vector&lt;br /&gt;
 :&lt;br /&gt;
 .job_handle&lt;br /&gt;
 PHA				; Make sure to preserve A&lt;br /&gt;
 CPY call			; Check call wanted against base of ours&lt;br /&gt;
 BCC job_handle_no		; Less than our base, so definately not ours&lt;br /&gt;
 TYA&lt;br /&gt;
 SBC #no_of_calls		; Subtract number of calls we have&lt;br /&gt;
 CMP call			; Now compare with base&lt;br /&gt;
 BCC job_handle2		; Yes, one of ours&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle.no&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (ovec2)			; Call back to old vector&lt;br /&gt;
 /&lt;br /&gt;
 .job_handle2&lt;br /&gt;
 TYA&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC call			; Subtract our base from call&lt;br /&gt;
 ASL A				; Times by 2 for offset into table&lt;br /&gt;
 TAY				; Place in Y&lt;br /&gt;
 LDA job_run_table,Y		; Get LSB of routine&lt;br /&gt;
 STA zero_gp1			; Store it for indirect jump (LSB)&lt;br /&gt;
 LDA job_run_table+1,Y		; Get MSB of routine&lt;br /&gt;
 STA zero_gp1+1		; Store it for indirect jump (MSB)&lt;br /&gt;
 PLA				; Restore A&lt;br /&gt;
 JMP (zero_gp1)		; Call our relevant routine&lt;br /&gt;
 :&lt;br /&gt;
 .job_names			; List of our JobNames&lt;br /&gt;
 EQUS &amp;quot;DemoJob&amp;quot;:EQUB 13	; Test job name&lt;br /&gt;
 /&lt;br /&gt;
 EQUB &amp;amp;FF			; &amp;amp;FF - marks end of table&lt;br /&gt;
 :&lt;br /&gt;
 .job_run_table		; List of routine addresses to match Jobs&lt;br /&gt;
 EQUW job_DemoJob		; Test job routine&lt;br /&gt;
 :&lt;br /&gt;
 .call   : EQUB 0		; Store for our JobCode base&lt;br /&gt;
 .job_id : EQUB 0		; Store for our JobId&lt;br /&gt;
 .ovec   : EQUW 0		; Store for old internal_vec value&lt;br /&gt;
 .ovec2  : EQUW 0		; Store for old unknownjob_vec value&lt;br /&gt;
 :&lt;br /&gt;
 EQUS STRING$(&amp;amp;100-(P% MOD 256),CHR$0)&lt;br /&gt;
 .end_of_code			; end_of_code should be on page boundry&lt;br /&gt;
 :&lt;br /&gt;
 .execute&lt;br /&gt;
 LDX #no_of_calls		; Number of JobCodes wanted&lt;br /&gt;
 LDA #1			; CALLOS, function 1, Claim JobCodes&lt;br /&gt;
 JSR OS_CALLOS			; Call CALLOS&lt;br /&gt;
 STX call			; Store base address of JobCodes allocated&lt;br /&gt;
 STY job_id			; Store JOB ID given&lt;br /&gt;
 CPX #0			; Check we were given some codes&lt;br /&gt;
 BEQ noinstall			; No, don't bother installing ourself&lt;br /&gt;
 /&lt;br /&gt;
 LDA internal_vec		; Get old internal_vec (LSB)&lt;br /&gt;
 STA ovec			; Store it&lt;br /&gt;
 LDA internal_vec+1		; Get old internal_vec (MSB)&lt;br /&gt;
 STA ovec+1			; Store it&lt;br /&gt;
 LDA #internal_handle MOD 256	; Our internal handler (LSB)&lt;br /&gt;
 STA internal_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #internal_handle DIV 256	; Our internal handler (MSB)&lt;br /&gt;
 STA internal_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDA unknownjob_vec		; Get old unknownjob_vec (LSB)&lt;br /&gt;
 STA ovec2			; Store it&lt;br /&gt;
 LDA unknownjob_vec+1		; Get old unknownjob_vec (MSB)&lt;br /&gt;
 STA ovec2+1			; Store it&lt;br /&gt;
 LDA #job_handle MOD 256	; Our unknown job handler (LSB)&lt;br /&gt;
 STA unknownjob_vec		; Place it in the vector (LSB)&lt;br /&gt;
 LDA #job_handle DIV 256	; Our unknown job handler (MSB)&lt;br /&gt;
 STA unknownjob_vec+1		; Place it in the vector (MSB)&lt;br /&gt;
 /&lt;br /&gt;
 LDX #end_of_code MOD 256	; End of code (LSB)&lt;br /&gt;
 LDY #end_of_code DIV 256	; End of code (MSB)&lt;br /&gt;
 STX jobin_buf			; Place it in jobin_buf (LSB)&lt;br /&gt;
 STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)&lt;br /&gt;
 LDA #65			; JobCode for WriteLomem&lt;br /&gt;
 JSR OS_DECODEJOB		; Call JobCall handler&lt;br /&gt;
 /&lt;br /&gt;
 .noinstall&lt;br /&gt;
 RTS				; End - return to OS&lt;br /&gt;
 :&lt;br /&gt;
 .execcall&lt;br /&gt;
 LDA #&amp;gt;(execute-address)	; Final execution address offset (LSB)&lt;br /&gt;
 STA zero_gp1&lt;br /&gt;
 LDA #&amp;lt;(execute-address)	; Final execution address offset (MSB)&lt;br /&gt;
 STA zero_gp1+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(BitMap-address)	; BitMap address offset (LSB)&lt;br /&gt;
 STA zero_gp2&lt;br /&gt;
 LDA #&amp;lt;(BitMap-address)	; BitMap address offset (MSB)&lt;br /&gt;
 STA zero_gp2+1&lt;br /&gt;
 /&lt;br /&gt;
 LDA #&amp;gt;(execcall-address)	; Length of data to relocate (LSB)&lt;br /&gt;
 STA zero_gp3&lt;br /&gt;
 LDA #&amp;lt;(execcall-address)	; Length of data to relocate (MSB)&lt;br /&gt;
 STA zero_gp3+1&lt;br /&gt;
 /&lt;br /&gt;
 TXA				; Execcall REAL address&lt;br /&gt;
 SEC&lt;br /&gt;
 SBC #&amp;gt;(execcall-address)	; Subrtract length&lt;br /&gt;
 TAX				; Put it back in X&lt;br /&gt;
 TYA				; Ditto for MSB&lt;br /&gt;
 SBC #&amp;lt;(execcall-address)&lt;br /&gt;
 TAY&lt;br /&gt;
 LDA #4			; CALLOS function for Relocate&lt;br /&gt;
 JMP OS_CALLOS			; Call CALLOS&lt;br /&gt;
 /&lt;br /&gt;
 .BitMap			; BitMap start&lt;br /&gt;
 :&lt;br /&gt;
 ]&lt;br /&gt;
 NEXT&lt;br /&gt;
 :&lt;br /&gt;
 OSCLI &amp;quot;SAVE code&amp;quot;+STR$create+&amp;quot; &amp;quot;+STR$~data%+&amp;quot; &amp;quot;+STR$~O%&lt;br /&gt;
 :&lt;br /&gt;
 NEXT&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_JobList&amp;diff=257</id>
		<title>SmartBox JobList</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=SmartBox_JobList&amp;diff=257"/>
		<updated>2023-11-01T04:37:53Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Created page with &amp;quot; &amp;lt;nowiki&amp;gt;OS 4 - 14.02.00   0		Blank 		Does nothing Send		Byte 0 = 0 Returns		Nothing  1		Version 		Read the operating system version number Send		Byte 0 = 1 Returns		Byte 0 = version number - low byte 		Byte 1 = version number - high byte Comments	Divide by 1000 to get the version number, eg. version 1.023 		would be returned as 1023  2		Reset 		Reset Smart Box Send		Byte 0 = 2 		Byte 1 = 254 or 255 Returns		Nothing Comments	Reset SmartBox from the host micro. Sending a...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; &amp;lt;nowiki&amp;gt;OS 4 - 14.02.00&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
0		Blank&lt;br /&gt;
		Does nothing&lt;br /&gt;
Send		Byte 0 = 0&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
&lt;br /&gt;
1		Version&lt;br /&gt;
		Read the operating system version number&lt;br /&gt;
Send		Byte 0 = 1&lt;br /&gt;
Returns		Byte 0 = version number - low byte&lt;br /&gt;
		Byte 1 = version number - high byte&lt;br /&gt;
Comments	Divide by 1000 to get the version number, eg. version 1.023&lt;br /&gt;
		would be returned as 1023&lt;br /&gt;
&lt;br /&gt;
2		Reset&lt;br /&gt;
		Reset Smart Box&lt;br /&gt;
Send		Byte 0 = 2&lt;br /&gt;
		Byte 1 = 254 or 255&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	Reset SmartBox from the host micro. Sending a 254 performs&lt;br /&gt;
		a soft reset (same as pressing reset), sending 255 does a&lt;br /&gt;
		hard reset (clears battery back RAM). Before sending&lt;br /&gt;
		anymore codes create a small delay while SmartBox resets&lt;br /&gt;
		various parts of hardware&lt;br /&gt;
&lt;br /&gt;
3		NameCode&lt;br /&gt;
		To obtain the operating system call number where the name&lt;br /&gt;
		is known&lt;br /&gt;
Send		Byte 0 = 3&lt;br /&gt;
		Byte 1 - n = ASCII characters of OS call name&lt;br /&gt;
		Byte n+1 = 13&lt;br /&gt;
Returns		Byte 0 = Operating system call number&lt;br /&gt;
&lt;br /&gt;
4		CodeName&lt;br /&gt;
		To obtain the name associated with an operating system&lt;br /&gt;
		call&lt;br /&gt;
Send		Byte 0 = 4&lt;br /&gt;
		Byte 1 = OS call number&lt;br /&gt;
Returns		StringCR = OS call name&lt;br /&gt;
&lt;br /&gt;
5		MultipleSetup&lt;br /&gt;
		To set the values that will be returned by MultipleRead&lt;br /&gt;
Send		Byte 0 = 5&lt;br /&gt;
		Byte 1 = Analogue channel 1&lt;br /&gt;
		Byte 2 = Analogue channel 2&lt;br /&gt;
		Byte 3 = Analogue channel 3&lt;br /&gt;
		Byte 4 = Analogue channel 4&lt;br /&gt;
		Byte 5 = Digitial inputs&lt;br /&gt;
		Byte 6 = Digital outputs&lt;br /&gt;
		Byte 7 = Motor outputs&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	This sets up the readings which will be returned when the&lt;br /&gt;
		call MultipleRead is made. If a byte = 1 the corresponding&lt;br /&gt;
		port will be returned, 0 = value not returned&lt;br /&gt;
&lt;br /&gt;
6		MultipleRead&lt;br /&gt;
		Returns multiple readings as defined using MultipleSetup&lt;br /&gt;
Send		Byte 0 = 6&lt;br /&gt;
Returns		Bytes as defined by MultipleSetup&lt;br /&gt;
Comments	This call returns readings from a number of ports as&lt;br /&gt;
		defined by MultipleSetup&lt;br /&gt;
&lt;br /&gt;
7		MultipleServer&lt;br /&gt;
		Constantly returns multiple readings as defined using&lt;br /&gt;
		MultipleSetup&lt;br /&gt;
Send		Byte 0 = 7&lt;br /&gt;
Returns		Bytes as defined by MultipleSetup&lt;br /&gt;
Comments	This call is similar to MultipleRead but continues to&lt;br /&gt;
		return readings until SmartBox receives the byte 123&lt;br /&gt;
&lt;br /&gt;
8		IdentSystem&lt;br /&gt;
		Read System Information&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		Byte 0 = table length&lt;br /&gt;
		Word = VIA&lt;br /&gt;
		Word = ACIA&lt;br /&gt;
		Word = ADC&lt;br /&gt;
		Word = AUX.PORT&lt;br /&gt;
		Word = jobs.status&lt;br /&gt;
		Word = jobin.buf&lt;br /&gt;
		Word = jobout.buf&lt;br /&gt;
		Byte = Processor ident&lt;br /&gt;
		Word = OS version number&lt;br /&gt;
		Byte = Hardware version number&lt;br /&gt;
		String10 = Name, padded to 10 chars&lt;br /&gt;
		Byte = Number of inputs&lt;br /&gt;
		Byte = Number of outputs&lt;br /&gt;
		Byte = Number of motors&lt;br /&gt;
		Byte = Number of analogues&lt;br /&gt;
		Byte = BBR support&lt;br /&gt;
		Byte = Short support&lt;br /&gt;
		Byte = Printer support&lt;br /&gt;
		Byte = Keypad support&lt;br /&gt;
		Byte = LCD x dim/support&lt;br /&gt;
		Byte = LCD y dim&lt;br /&gt;
OS Release	3,4&lt;br /&gt;
&lt;br /&gt;
9		Credits&lt;br /&gt;
		Returns the copyright string&lt;br /&gt;
Send		Byte 0 = 9&lt;br /&gt;
Returns		StringNUL = Copyright string&lt;br /&gt;
Comments	This call returns the copyright message and OS details&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
10		WriteMotors&lt;br /&gt;
		Writes a byte to the motor drivers&lt;br /&gt;
Send		Byte 0 = 10&lt;br /&gt;
		Byte 1 = value to write&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	Each of the four motor outputs has two bits encoded into&lt;br /&gt;
		this value, bits 0 and 1 are for motor a, bytes 2 and 3 are&lt;br /&gt;
		for motor b, bits 4 and 5 are for motor c and bits 6 and 7&lt;br /&gt;
		are for motor d, setting both bits to 0 will stop the&lt;br /&gt;
		motor, setting the low bit high and high bit low will make&lt;br /&gt;
		the motor go forward and setting the high bit high and the&lt;br /&gt;
		low bit low will make it go backwards. Using this call&lt;br /&gt;
		automatically stops all pulsing of motors&lt;br /&gt;
&lt;br /&gt;
11		ReadMotors&lt;br /&gt;
		Read the state of the motor drivers&lt;br /&gt;
Send		Byte 0 = 11&lt;br /&gt;
Returns		Byte 0 = value read from the motor drivers&lt;br /&gt;
Comments	The value returned is the same value as what would be sent&lt;br /&gt;
		to WriteMotors&lt;br /&gt;
&lt;br /&gt;
12		MotorForward&lt;br /&gt;
		Switch a motor on&lt;br /&gt;
Send		Byte 0 = 12&lt;br /&gt;
		Byte 1 = motor number (1 to 4)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	This makes the appropiate motor go forward. Using this call&lt;br /&gt;
		automatically cancels the pulsing for the appropiate motor&lt;br /&gt;
&lt;br /&gt;
13		MotorReverse&lt;br /&gt;
		Switch a motor on with reverse polarity&lt;br /&gt;
Send		Byte 0 = 13&lt;br /&gt;
		Byte 1 = motor number (1 to 4)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	This makes the appropiate motor go backward. Using this&lt;br /&gt;
		call automatically cancels the pulsing for the appropiate&lt;br /&gt;
		motor&lt;br /&gt;
&lt;br /&gt;
14		MotorHalt&lt;br /&gt;
		Switch a motor off&lt;br /&gt;
Send		Byte 0 = 14&lt;br /&gt;
		Byte 1 = motor number (1 to 4)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	This makes the appropiate motor stop. Using this call&lt;br /&gt;
		automatically cancels the pulsing for the appropiate motor&lt;br /&gt;
&lt;br /&gt;
15		MotorPower&lt;br /&gt;
		Pulse a motor outputs to vary speed&lt;br /&gt;
Send		Byte 0 = 15&lt;br /&gt;
		Byte 1 = motor number (1 to 4)&lt;br /&gt;
		Byte 2 = on time&lt;br /&gt;
		Byte 3 = off time&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	This pulses the appropiate motor at a rate determined by&lt;br /&gt;
		the on/off time (in centiseconds) specified, alternating&lt;br /&gt;
		the motor between the state at which it was last defined&lt;br /&gt;
		(Forward or Reverse) and Halt&lt;br /&gt;
&lt;br /&gt;
16		PatchMF&lt;br /&gt;
		Same as MotorForward&lt;br /&gt;
OS Release	3,4&lt;br /&gt;
&lt;br /&gt;
17		MotorVoltage&lt;br /&gt;
		Set voltage for motor outputs&lt;br /&gt;
Send		Byte 0 = voltage (0 to 3)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Notes		Voltage values are 0 (0v), 1 (6v), 2 (9v), 3 (12v).&lt;br /&gt;
		*Setting a voltage of 0 will also switch *outputs* to 0v*&lt;br /&gt;
Comments	This sets the actual voltage supplied to the motor outputs&lt;br /&gt;
OS Release	4&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
20		WriteOutputs&lt;br /&gt;
		Write a 8 bit value to the digital output port&lt;br /&gt;
Send		Byte 0 = 20&lt;br /&gt;
		Byte 1 = byte to be written to the port&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	Using this call automatically cancels all pulsing on the&lt;br /&gt;
		output port&lt;br /&gt;
&lt;br /&gt;
21		OutputPower&lt;br /&gt;
		Vary the power by pulsing individual output lines&lt;br /&gt;
Send		Byte 0 = 21&lt;br /&gt;
		Byte 1 = bit number (0 to 7)&lt;br /&gt;
		Byte 2 = on time&lt;br /&gt;
		Byte 3 = off time&lt;br /&gt;
Comments	This pulses the appropiate output line at a rate determined&lt;br /&gt;
		by the on/off time (in centiseconds) specified, alternating&lt;br /&gt;
		the output line	between on and off&lt;br /&gt;
&lt;br /&gt;
22		GetSensors&lt;br /&gt;
		To read the type of sensors connected to the analogue&lt;br /&gt;
		sensors&lt;br /&gt;
Send		Byte 0 = 22&lt;br /&gt;
Returns		Byte 1 = type of sensor connected to sensor A&lt;br /&gt;
		Byte 2 = type of sensor connected to sensor B&lt;br /&gt;
		Byte 3 = type of sensor connected to sensor C&lt;br /&gt;
		Byte 4 = type of sensor connected to sensor D&lt;br /&gt;
Comments	This returns the sensor types connected to the analogue&lt;br /&gt;
		sensors	that were returned from the last time they were&lt;br /&gt;
		checked. A sensor type of 0 means no sensor&lt;br /&gt;
&lt;br /&gt;
23		CheckSensors&lt;br /&gt;
		To check the type of sensors connected to the analogue&lt;br /&gt;
		sensors&lt;br /&gt;
Send		Byte 0 = 23&lt;br /&gt;
Returns		Byte 1 = type of sensor connected to sensor A&lt;br /&gt;
		Byte 2 = type of sensor connected to sensor B&lt;br /&gt;
		Byte 3 = type of sensor connected to sensor C&lt;br /&gt;
		Byte 4 = type of sensor connected to sensor D&lt;br /&gt;
Comments	This does an immediate check of the sensor types connected&lt;br /&gt;
		to the analogue sensors. A sensor type of 0 means no&lt;br /&gt;
		sensor&lt;br /&gt;
&lt;br /&gt;
24		WriteSensorTable&lt;br /&gt;
		Write sensor table entry&lt;br /&gt;
Notes		* Not Implemented *&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
25		ReadSensorTable&lt;br /&gt;
		Read sensor table entry&lt;br /&gt;
Send		Byte 0 = entry to read&lt;br /&gt;
Returns		Byte 0 = sensor number&lt;br /&gt;
		Byte 1 = number of decimal points to display&lt;br /&gt;
		Byte 2 = max adc reading&lt;br /&gt;
		Byte 3 = reading offset (LSB)&lt;br /&gt;
		Byte 4 = reading offset (MSB)&lt;br /&gt;
		Byte 5 = multiplication factor (LSB)&lt;br /&gt;
		Byte 6 = multiplication factor (MSB)&lt;br /&gt;
		Byte 7 = division factor (LSB)&lt;br /&gt;
		Byte 8 = division factor (LSB)&lt;br /&gt;
		String = full sensor title&lt;br /&gt;
		String = abbreviated sensor label&lt;br /&gt;
		String = sensor label&lt;br /&gt;
		String = sensor units&lt;br /&gt;
Notes		If byte 0 is &amp;amp;FF then sensor is unknown&lt;br /&gt;
OS Release	3,4&lt;br /&gt;
&lt;br /&gt;
28		SetBitHigh&lt;br /&gt;
		Set individual output line(s) high&lt;br /&gt;
Send		Byte 0 = 28&lt;br /&gt;
		Byte 1 = byte determining which lines will be set&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	A bit set in the byte sent will set the corresponding&lt;br /&gt;
		output line high&lt;br /&gt;
&lt;br /&gt;
29		SetBitLow&lt;br /&gt;
		Set individual output line(s) low&lt;br /&gt;
Send		Byte 0 = 29&lt;br /&gt;
		Byte 1 = byte determining which lines will be unset&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	A bit set in the byte sent will set the corresponding&lt;br /&gt;
		output line low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
30		ReadADCReg&lt;br /&gt;
		To read a ADC register&lt;br /&gt;
Send		Byte 0 = 30&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
Returns		Byte 0 = value of register&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
31		WriteADCReg&lt;br /&gt;
		To write to a ADC register&lt;br /&gt;
Send		Byte 0 = 31&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
		Byte 2 = value to write&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
32		ReadACIAReg&lt;br /&gt;
		To read a ACIA register&lt;br /&gt;
Send		Byte 0 = 32&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
Returns		Byte 0 = value of register&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
33		WriteACIAReg&lt;br /&gt;
		To write to a ACIA register&lt;br /&gt;
Send		Byte 0 = 33&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
		Byte 2 = value to write&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
34		ReadVIAReg&lt;br /&gt;
		To read a VIA register&lt;br /&gt;
Send		Byte 0 = 34&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
Returns		Byte 0 = value of register&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
35		WriteVIAReg&lt;br /&gt;
		To write to a VIA register&lt;br /&gt;
Send		Byte 0 = 35&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
		Byte 2 = value to write&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
36		SetVIAHigh&lt;br /&gt;
		To set bits in a VIA register&lt;br /&gt;
Send		Byte 0 = 36&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
		Byte 2 = mask&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
37		SetVIALow&lt;br /&gt;
		To unset bits in a VIA register&lt;br /&gt;
Send		Byte 0 = 37&lt;br /&gt;
		Byte 1 = register number (0 to 15)&lt;br /&gt;
		Byte 2 = mask&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
40		ReadADC&lt;br /&gt;
		Take a reading from a specific ADC channel&lt;br /&gt;
Send		Byte 0 = 40&lt;br /&gt;
		Byte 1 = channel number (1 to 4)&lt;br /&gt;
Returns		For an 8 bit reading:&lt;br /&gt;
			Byte 0 = reading from ADC&lt;br /&gt;
		For a 16 bit reading:&lt;br /&gt;
			Byte 0 = low byte of reading&lt;br /&gt;
			Byte 1 = high byte of reading&lt;br /&gt;
Comments	The value returned will be at the resolution specified by&lt;br /&gt;
		OS calls HighResADC and LowResADC&lt;br /&gt;
&lt;br /&gt;
41		ReadADCs&lt;br /&gt;
		Read all the ADC channels&lt;br /&gt;
Send		Byte 0 = 41&lt;br /&gt;
Returns		For 8 bit readings:&lt;br /&gt;
			Byte 0 = reading from channel 1&lt;br /&gt;
			Byte 1 = reading from channel 2&lt;br /&gt;
			Byte 2 = reading from channel 3&lt;br /&gt;
			Byte 3 = reading from channel 4&lt;br /&gt;
		For 16 bit readings&lt;br /&gt;
			Byte 0 = reading from channel 1 (low byte)&lt;br /&gt;
			Byte 1 = reading from channel 1 (high byte)&lt;br /&gt;
			Byte 2 = reading from channel 2 (low byte)&lt;br /&gt;
			Byte 3 = reading from channel 2 (high byte)&lt;br /&gt;
			Byte 4 = reading from channel 3 (low byte)&lt;br /&gt;
			Byte 5 = reading from channel 3 (high byte)&lt;br /&gt;
			Byte 6 = reading from channel 4 (low byte)&lt;br /&gt;
			Byte 7 = reading from channel 4 (high byte)&lt;br /&gt;
&lt;br /&gt;
42		ForcedADCRead&lt;br /&gt;
		Force the A to D convertor to make a conversion and return&lt;br /&gt;
		the result&lt;br /&gt;
Send		Byte 0 = 42&lt;br /&gt;
		Byte 1 = channel number (1 to 4)&lt;br /&gt;
Returns		For an 8 bit reading:&lt;br /&gt;
			Byte 0 = reading from ADC&lt;br /&gt;
		For a 16 bit reading&lt;br /&gt;
			Byte 0 = reading from ADC (low byte)&lt;br /&gt;
			Byte 1 = reading from ADC (high byte)&lt;br /&gt;
&lt;br /&gt;
44		HighResADC&lt;br /&gt;
		Sets the resolution of ADC readings to 16 bit&lt;br /&gt;
		Subsequent readings will return 2 byte values&lt;br /&gt;
Send		Byte 0 = 44&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	For OS 2 readings returned will be only accurate to&lt;br /&gt;
		10 bits. OS 3,4 provides support for 16 bit readings,&lt;br /&gt;
		but only to an accuracy of 8 bits.&lt;br /&gt;
&lt;br /&gt;
45		LowResADC&lt;br /&gt;
		Sets the resolution of ADC readings to 8 bit&lt;br /&gt;
		Subsequent readings will return single byte values&lt;br /&gt;
Send		Byte 0 = 45&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
&lt;br /&gt;
47		ReadResolution&lt;br /&gt;
		Reads the current ADC resolution setting&lt;br /&gt;
Send		Byte 0 = 47&lt;br /&gt;
Returns		Byte 0 = resolution setting, where:&lt;br /&gt;
			0 = 8 bit&lt;br /&gt;
			1 = 16 bit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
50		DownloadData (OS 2), DownloadData740 (OS 3),&lt;br /&gt;
		DownloadData375 (OS 4)&lt;br /&gt;
		Download data into the SmartBox's memory&lt;br /&gt;
Send		Byte 0 = 50&lt;br /&gt;
		Byte 1 = start address to write (low byte)&lt;br /&gt;
		Byte 2 = start address to write (high byte)&lt;br /&gt;
		Byte 3 = length of data (low byte)&lt;br /&gt;
		Byte 4 = length of data (high byte)&lt;br /&gt;
		Bytes 5 - n = data&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
&lt;br /&gt;
52		UploadData (OS 2), UploadData740 (OS 3),&lt;br /&gt;
		UploadData (OS 4)&lt;br /&gt;
		Upload data from the SmartBox's memory&lt;br /&gt;
Send		Byte 0 = 52&lt;br /&gt;
		Byte 1 = start address to read (low byte)&lt;br /&gt;
		Byte 2 = start address to read (high byte)&lt;br /&gt;
		Byte 3 = length of data (low byte)&lt;br /&gt;
		Byte 4 = length of data (high byte)&lt;br /&gt;
Returns		Byte 0 - n = data&lt;br /&gt;
&lt;br /&gt;
54		ExecuteCode (OS 2), ExecuteCode740 (OS 3),&lt;br /&gt;
		ExecuteCode375 (OS 4)&lt;br /&gt;
		Execute machine code held at a specified address&lt;br /&gt;
Send		Byte 0 = 54&lt;br /&gt;
		Byte 1 = execution address (low byte)&lt;br /&gt;
		Byte 2 = execution address (high byte)&lt;br /&gt;
		Byte 3 = contents of the A register on entry to the code&lt;br /&gt;
		Byte 4 = contents of the X register on entry to the code&lt;br /&gt;
		Byte 5 = contents of the Y register on entry to the code&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
&lt;br /&gt;
55		StoreByte (OS 2), StoreByte740 (OS 3),&lt;br /&gt;
		StoreByte375 (OS 4)&lt;br /&gt;
		Store a byte in the SmartBox's RAM&lt;br /&gt;
Send		Byte 0 = 55&lt;br /&gt;
		Byte 1 = address to write (low byte)&lt;br /&gt;
		Byte 2 = address to write (high byte)&lt;br /&gt;
		Byte 3 = byte to be stored&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
&lt;br /&gt;
56		ReadByte (OS 2), ReadByte740 (OS 3),&lt;br /&gt;
		ReadByte (OS 4)&lt;br /&gt;
		Read a byte from the SmartBox's RAM&lt;br /&gt;
Send		Byte 0 = 56&lt;br /&gt;
		Byte 1 = address to read (low byte)&lt;br /&gt;
		Byte 2 = address to read (high byte)&lt;br /&gt;
Returns		Byte 0 = byte read&lt;br /&gt;
&lt;br /&gt;
57		ReadRAMSize&lt;br /&gt;
		Read the amount of RAM with which the SmartBox is fitted&lt;br /&gt;
Send		Byte 0 = 57&lt;br /&gt;
Returns		Byte 0 = RAM size (low byte)&lt;br /&gt;
		Byte 1 = RAM size (high byte)&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
59		ExtendCall&lt;br /&gt;
		Call the extended call vector&lt;br /&gt;
Send		Byte 0 = 59&lt;br /&gt;
Returns		Byte 0 = extension value&lt;br /&gt;
Comments	This call provides the user with the possibility of adding&lt;br /&gt;
		extra calls to SmartBox easily&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
60		SetClock&lt;br /&gt;
		Set the internal clock in SmartBox. This clock only runs&lt;br /&gt;
		while the power in maintained&lt;br /&gt;
Send		Byte 0 = 60&lt;br /&gt;
		Byte 1 = 1/10 seconds (0 to 9)&lt;br /&gt;
		Byte 2 = seconds (0 to 59)&lt;br /&gt;
		Byte 3 = minutes (0 to 59)&lt;br /&gt;
		Byte 4 = hours (0 to 23)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Comments	The day value is set to 0&lt;br /&gt;
&lt;br /&gt;
61		ReadClock&lt;br /&gt;
		Read the internal clock. On reset the clock will be set to&lt;br /&gt;
		zero&lt;br /&gt;
Send		Byte 0 = 61&lt;br /&gt;
Returns		Byte 0 = 1/10 seconds&lt;br /&gt;
		Byte 1 = seconds&lt;br /&gt;
		Byte 2 = minutes&lt;br /&gt;
		Byte 3 = hours&lt;br /&gt;
		Byte 4 = days&lt;br /&gt;
&lt;br /&gt;
62		ReadTopmem&lt;br /&gt;
		Read the current value of TOPMEM&lt;br /&gt;
Send		Byte 0 = 62&lt;br /&gt;
Returns		Byte 0 = value of TOPMEM (low byte)&lt;br /&gt;
		Byte 1 = value of TOPMEM (high byte)&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
63		WriteTopmem&lt;br /&gt;
		Write the value of TOPMEM&lt;br /&gt;
Send		Byte 0 = 63&lt;br /&gt;
		Byte 1 = value of TOPMEM (low byte)&lt;br /&gt;
		Byte 2 = value of TOPMEM (high byte)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
64		ReadLomem&lt;br /&gt;
		Read the current value of LOMEM&lt;br /&gt;
Send		Byte 0 = 64&lt;br /&gt;
Returns		Byte 0 = value of LOMEM (low byte)&lt;br /&gt;
		Byte 1 = value of LOMEM (high byte)&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
65		WriteLomem&lt;br /&gt;
		Write the value of LOMEM&lt;br /&gt;
Send		Byte 0 = 65&lt;br /&gt;
		Byte 1 = value of LOMEM (low byte)&lt;br /&gt;
		Byte 2 = value of LOMEM (high byte)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
66		ReadHimem&lt;br /&gt;
		Read the current value of HIMEM&lt;br /&gt;
Send		Byte 0 = 66&lt;br /&gt;
Returns		Byte 0 = value of HIMEM (low byte)&lt;br /&gt;
		Byte 1 = value of HIMEM (high byte)&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
67		WriteHimem&lt;br /&gt;
		Write the value of HIMEM&lt;br /&gt;
Send		Byte 0 = 67&lt;br /&gt;
		Byte 1 = value of HIMEM (low byte)&lt;br /&gt;
		Byte 2 = value of HIMEM (high byte)&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	2,3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
70		WritePrinter&lt;br /&gt;
		Write printer port (no handshaking)&lt;br /&gt;
Send		Byte 0 = byte to write&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
71		ReadPrinter&lt;br /&gt;
		Read printer port&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		Byte 0 = printer port value&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
72		PrintChar&lt;br /&gt;
		Send a character to printer port&lt;br /&gt;
Send		Byte 0 = char&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
73		PrintStreamZ&lt;br /&gt;
		Send a stream of characters to the printer port&lt;br /&gt;
Send		StringNUL = string to print&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
74		PrintStream&lt;br /&gt;
		Send a stream of characters to the printer port&lt;br /&gt;
Send		Byte 0 = number of bytes to send (LSB)&lt;br /&gt;
		Byte 1 = number of bytes to send (MSB)&lt;br /&gt;
		Bytes = stream of characters to print&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
75		PrintServer&lt;br /&gt;
		Echo all characters to printer port&lt;br /&gt;
Notes		No exit		&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
80		WriteRTCReg&lt;br /&gt;
		Write a RTC register&lt;br /&gt;
Send		Byte 0 = register&lt;br /&gt;
		Byte 1 = value&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
81		ReadRTCReg&lt;br /&gt;
		Read a RTC register&lt;br /&gt;
Send		Byte 0 = register&lt;br /&gt;
Returns		Byte 0 = value&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
82		WriteRTC&lt;br /&gt;
		Writes RTC as a string&lt;br /&gt;
Send		StringCR = RTC time&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Notes		* Not currently implemented *&lt;br /&gt;
		Time is represented as DDD,dd mmm yyyy.hh:mm:ss&lt;br /&gt;
		DDD is 3 character day (Mon, Tue, Wed, Thu, Fri, Sat, Sun)&lt;br /&gt;
		dd is day of month&lt;br /&gt;
		mmm is month&lt;br /&gt;
		yyyy is year&lt;br /&gt;
		hh is hours&lt;br /&gt;
		mm is minutes&lt;br /&gt;
		ss is seconds&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
83		ReadRTC&lt;br /&gt;
		Reads RTC as a string&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		StringCR = RTC time&lt;br /&gt;
Notes		Time is represented as DDD,dd mmm yyyy.hh:mm:ss&lt;br /&gt;
		DDD is 3 character day (Mon, Tue, Wed, Thu, Fri, Sat, Sun)&lt;br /&gt;
		dd is day of month&lt;br /&gt;
		mmm is month&lt;br /&gt;
		yyyy is year&lt;br /&gt;
		hh is hours&lt;br /&gt;
		mm is minutes&lt;br /&gt;
		ss is seconds&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
84		WriteRTCbcd&lt;br /&gt;
		Writes all 7 RTC time registers&lt;br /&gt;
Send		Byte 0 = seconds&lt;br /&gt;
		Byte 1 = minutes&lt;br /&gt;
		Byte 2 = hours&lt;br /&gt;
		Byte 3 = date&lt;br /&gt;
		Byte 4 = month&lt;br /&gt;
		Byte 5 = day&lt;br /&gt;
		Byte 6 = year&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
Notes		All values are represented in BCD&lt;br /&gt;
		Years below 80 (&amp;amp;80 in BCD) signify 20xx&lt;br /&gt;
		Day starts (0) from Monday&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
85		ReadRTCbcd&lt;br /&gt;
		Reads all 7 RTC time registers&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		Byte 0 = seconds&lt;br /&gt;
		Byte 1 = minutes&lt;br /&gt;
		Byte 2 = hours&lt;br /&gt;
		Byte 3 = date&lt;br /&gt;
		Byte 4 = month&lt;br /&gt;
		Byte 5 = day&lt;br /&gt;
		Byte 6 = year&lt;br /&gt;
Notes		All values are represented in BCD&lt;br /&gt;
		Years below 80 (&amp;amp;80 in BCD) signify 20xx&lt;br /&gt;
		Day starts (0) from Monday&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
90		ReadInputs&lt;br /&gt;
		Reads a byte from the digital inputs port&lt;br /&gt;
Send		Byte 0 = 90&lt;br /&gt;
Returns		Byte 0 = byte read from the digital inputs port&lt;br /&gt;
&lt;br /&gt;
91		ReadBit&lt;br /&gt;
		Reads a bit from the digital inputs port&lt;br /&gt;
Send		Byte 0 = 91&lt;br /&gt;
		Byte 1 = byte to read (0 to 7)&lt;br /&gt;
Returns		Byte 0 = bit read from the digital inputs port&lt;br /&gt;
Comments	Reads an individual sensor from the digital inputs port&lt;br /&gt;
&lt;br /&gt;
92		ReadOutputs&lt;br /&gt;
		Reads a byte from the digital outputs port&lt;br /&gt;
Send		Byte 0 = 92&lt;br /&gt;
Returns		Byte 0 = byte read from the digital outputs port&lt;br /&gt;
Notes		This reads the soft copy of the last written byte&lt;br /&gt;
OS Release	3,4&lt;br /&gt;
&lt;br /&gt;
93		CountReset&lt;br /&gt;
		Resets input port counter(s)&lt;br /&gt;
Send		Byte 0 = 93&lt;br /&gt;
		Byte 1 = bit masks for counters to reset&lt;br /&gt;
OS Release	4&lt;br /&gt;
&lt;br /&gt;
94		CountRead&lt;br /&gt;
		Reads input port counter(s)&lt;br /&gt;
Send		Byte 0 = 94&lt;br /&gt;
		Byte 1 = counter to read (0 to 7) or 255 for all&lt;br /&gt;
Returns		Byte 0 = value of counter (LSB)&lt;br /&gt;
		Byte 1 = value of counter (MSB)&lt;br /&gt;
		Byte n = value of counter (LSB)&lt;br /&gt;
		Byte n+1 = value of counter (MSB)&lt;br /&gt;
Notes		Counter values return 65535 (-1) from box reset, or max count&lt;br /&gt;
OS Release	4&lt;br /&gt;
&lt;br /&gt;
98		InsightDriver&lt;br /&gt;
		Starts the internal Insight software&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
99		DataApp&lt;br /&gt;
		Starts the internal DataApp software&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
100		WriteLCDReg&lt;br /&gt;
		Write LCD Register&lt;br /&gt;
Send		Byte 0 = register number&lt;br /&gt;
		Byte 1 = register value&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
101		ReadLCDReg&lt;br /&gt;
		Read LCD Register&lt;br /&gt;
Send		Byte 0 = register number&lt;br /&gt;
Returns		Byte 0 = register value&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
102		LCDChar&lt;br /&gt;
		Send a character to the LCD display driver&lt;br /&gt;
Send		Byte 0 = char&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
103		LCDStreamZ&lt;br /&gt;
		Send a stream of characters to the LCD display driver&lt;br /&gt;
Send		StringNUL = string to display&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
104		LCDStream&lt;br /&gt;
		Send a stream of characters to the LCD display driver&lt;br /&gt;
Send		Byte 0 = number of bytes to send (LSB)&lt;br /&gt;
		Byte 1 = number of bytes to send (MSB)&lt;br /&gt;
		Bytes = stream of characters to display&lt;br /&gt;
Returns		Nothing&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
110		ReadKeypad&lt;br /&gt;
		Reads the raw state of the keypad&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		Byte 0 = current keypad reading bitstate&lt;br /&gt;
OS Release	3&lt;br /&gt;
&lt;br /&gt;
111		ReadKeypadPress&lt;br /&gt;
		Reads the processed state of the keypad&lt;br /&gt;
Send		Nothing&lt;br /&gt;
Returns		Byte 0 = current keypad press bitstate&lt;br /&gt;
OS Release	3&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Main_Page&amp;diff=256</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Main_Page&amp;diff=256"/>
		<updated>2023-11-01T04:32:51Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Smart Box User Guide ==&lt;br /&gt;
&lt;br /&gt;
Information has been adapted from the original ''Smart Box'' and ''Smart Move'' user manual, copyright Economatics (Education) Limited 1992.&lt;br /&gt;
&lt;br /&gt;
* [[Smart Box]] - A guide to using the Smart Box, including the Smart Box ''EV''.&lt;br /&gt;
* [[Tutorial]] - Tutorial for the Smart Move software.&lt;br /&gt;
* [[Reference]] - A general reference guide to Smart Move commands.&lt;br /&gt;
* [[Smart Sensors and Relay]] - Information sheets on individual sensors and input/output devices.&lt;br /&gt;
&lt;br /&gt;
== Technical Information ==&lt;br /&gt;
&lt;br /&gt;
* [[Serial Port]] - Pinout for the Smart Box's serial port.&lt;br /&gt;
* [[Analogue Sensor Ports]] - Pinout for the four analogue sensors.&lt;br /&gt;
&lt;br /&gt;
The following pages have been adapted from information shared at https://github.com/Phipli/SmartBox/issues/&lt;br /&gt;
&lt;br /&gt;
* [[SmartBox JobList]] - A list of the available jobs (routines) provided by the Smart Box OS that can be invoked from the serial link.&lt;br /&gt;
* [[SmartBox OS]] - Information about the organisation of the operating system and development of additional jobs.&lt;br /&gt;
* [[SmartBox AlbertLink]] - Details of the AlbertLink job used by the Smart Move software.&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Digital_Sensors&amp;diff=255</id>
		<title>Digital Sensors</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Digital_Sensors&amp;diff=255"/>
		<updated>2023-10-25T20:45:28Z</updated>

		<summary type="html">&lt;p&gt;Benryves: /* An example project */ Added Macintosh monitor window graphic&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The range of digital sensors allow simple on / off conditions to be detected.&lt;br /&gt;
The sensors are connected between the pairs of red and yellow digital sensor sockets on the top of the box.&lt;br /&gt;
When the sensor is in its ‘On’ condition the indicator on the box will light.&lt;br /&gt;
The Smart Move software can detect the condition of the switch as ‘On’ or ‘Off’ or alternatively ‘1’ or ‘0’.&lt;br /&gt;
Both these Smart Move program lines are valid:&lt;br /&gt;
&lt;br /&gt;
 IF SENSOR 1 IS ON THEN FORWARD A&lt;br /&gt;
 IF SENSOR 1 = 1 THEN FORWARD A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Sensor-Push-Switch.svg|A '''Push Switch''' can be used as a manual input device. The switch is normally ‘Off’ and will be detected as ‘On’ when pressed.&lt;br /&gt;
File:Sensor-Tilt-Switch.svg|A '''Tilt switch''' can be used to detect when an object has moved beyond a particular angle.&lt;br /&gt;
File:Sensor-Magnetic-Switch.svg|A '''Magnetic Switch''' will be ‘On’ when a magnet is close to the end.&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Making your own sensors ==&lt;br /&gt;
&lt;br /&gt;
Any device which completes a circuit between a red and yellow socket on Smart Box can be used as a digital sensor.&lt;br /&gt;
This can be as simple as two contacts closing together.&lt;br /&gt;
Switches can be manufactured using card and tin foil, pegs and drawing pins etc.&lt;br /&gt;
&lt;br /&gt;
== An example project ==&lt;br /&gt;
&lt;br /&gt;
The following example uses three digital sensors and a buzzer to create a simple security system.&lt;br /&gt;
&lt;br /&gt;
Sensors 0 and 1 are magnetic contact switches which will open as a door or window is opened.&lt;br /&gt;
A push switch is connected to sensor 2 which is used to switch off the system.&lt;br /&gt;
The Digital sensors can be ‘Labelled’ or given an alternative name using the LABEL command in Smart Move.&lt;br /&gt;
This makes the sensors easier to identify.&lt;br /&gt;
In the same way some of the Smart Move keywords can be changed.&lt;br /&gt;
In this example the keyword OFF is changed to OPEN.&lt;br /&gt;
The [[LABEL|reference section]] gives more details.&lt;br /&gt;
&lt;br /&gt;
[[File:Monitor-Alarm-Sensor-Labels.png|frame|right|Macintosh monitor window showing labels]]&lt;br /&gt;
&lt;br /&gt;
Type in the following as direct commands&lt;br /&gt;
&lt;br /&gt;
 LABEL OFF open&lt;br /&gt;
 LABEL SENSOR0 door&lt;br /&gt;
 LABEL SENSOR1 window&lt;br /&gt;
 LABEL SENSOR2 turnoff&lt;br /&gt;
 LABEL OUTPUT0 buzzer&lt;br /&gt;
&lt;br /&gt;
Type in all the procedures as shown then run the ''Alarm'' procedure.&lt;br /&gt;
&lt;br /&gt;
=== Procedure: Alarm ===&lt;br /&gt;
&lt;br /&gt;
 REPEAT&lt;br /&gt;
 IF DOOR IS OPEN THEN Soundalarm&lt;br /&gt;
 IF WINDOW IS OPEN THEN Soundalarm&lt;br /&gt;
 UNTIL TURNOFF IS ON&lt;br /&gt;
 Stopalarm&lt;br /&gt;
&lt;br /&gt;
=== Procedure: Soundalarm ===&lt;br /&gt;
&lt;br /&gt;
 SWITCH ON buzzer PULSE 50,50&lt;br /&gt;
&lt;br /&gt;
=== Procedure: Stopalarm ===&lt;br /&gt;
&lt;br /&gt;
 SWITCH OFF buzzer&lt;br /&gt;
&lt;br /&gt;
[[Category:Sensors]]&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Monitor-Alarm-Sensor-Labels.png&amp;diff=254</id>
		<title>File:Monitor-Alarm-Sensor-Labels.png</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Monitor-Alarm-Sensor-Labels.png&amp;diff=254"/>
		<updated>2023-10-25T20:43:12Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Macintosh monitor window showing labels&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-7-Segment-Display.svg&amp;diff=253</id>
		<title>File:Output-7-Segment-Display.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-7-Segment-Display.svg&amp;diff=253"/>
		<updated>2023-10-24T00:22:03Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Benryves uploaded a new version of File:Output-7-Segment-Display.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
7-Segment display board&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-7-Segment-Display-Connection.svg&amp;diff=252</id>
		<title>File:Output-7-Segment-Display-Connection.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-7-Segment-Display-Connection.svg&amp;diff=252"/>
		<updated>2023-10-24T00:21:27Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Benryves uploaded a new version of File:Output-7-Segment-Display-Connection.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Connections between a 7-segment display board and the Smart Box&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Tutorial-Printing-Procedures.svg&amp;diff=251</id>
		<title>File:Tutorial-Printing-Procedures.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Tutorial-Printing-Procedures.svg&amp;diff=251"/>
		<updated>2023-10-24T00:09:19Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Benryves uploaded a new version of File:Tutorial-Printing-Procedures.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Printer printing a single procedure&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Printing_procedures&amp;diff=250</id>
		<title>Printing procedures</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Printing_procedures&amp;diff=250"/>
		<updated>2023-10-24T00:08:48Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added the missing printer image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;To print your procedures first check that your printer is on line and ready.&lt;br /&gt;
Type LIST followed by the procedure name.&lt;br /&gt;
The procedure will be printed to your printer.&lt;br /&gt;
&lt;br /&gt;
[[File:Tutorial-Printing-Procedures.svg|frameless]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorial]]&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Tutorial-Printing-Procedures.svg&amp;diff=249</id>
		<title>File:Tutorial-Printing-Procedures.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Tutorial-Printing-Procedures.svg&amp;diff=249"/>
		<updated>2023-10-24T00:07:28Z</updated>

		<summary type="html">&lt;p&gt;Benryves: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Printer printing a single procedure&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=Template:Output_gallery&amp;diff=248</id>
		<title>Template:Output gallery</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=Template:Output_gallery&amp;diff=248"/>
		<updated>2023-10-23T23:42:28Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added double-pole relay&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Output-Relay.svg|link=Smart Box Relays|Relay&lt;br /&gt;
File:Output-Relay-Double-Pole.svg|link=Smart Box Relays|Double-pole relay&lt;br /&gt;
File:Output-7-Segment-Display.svg|link=7 Segment Display|7 Segment Display&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
	<entry>
		<id>https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-Relay-Double-Pole.svg&amp;diff=247</id>
		<title>File:Output-Relay-Double-Pole.svg</title>
		<link rel="alternate" type="text/html" href="https://smartbox.benryves.com/mediawiki/index.php?title=File:Output-Relay-Double-Pole.svg&amp;diff=247"/>
		<updated>2023-10-23T23:41:36Z</updated>

		<summary type="html">&lt;p&gt;Benryves: Added a picture of a double-pole relay&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Added a picture of a double-pole relay&lt;/div&gt;</summary>
		<author><name>Benryves</name></author>
	</entry>
</feed>