SmartBox OS: Difference between revisions

From Smart Box
Jump to navigation Jump to search
m (→‎Memory: Put zero page data in a table)
m (→‎Changelog: Put changelog in table)
 
(10 intermediate revisions by the same user not shown)
Line 172: Line 172:
in and WHY.
in and WHY.


=== Serial Port ===
=== OS_READBYTE and OS_SENDBYTE: Serial Port ===


The serial port on the controller is currently based on the 6850
The serial port on the controller is currently based on the 6850
Line 185: Line 185:


As said, there are two calls OS_READBYTE and OS_SENDBYTE.  
As said, there are two calls OS_READBYTE and OS_SENDBYTE.  
==== OS_READBYTE ====
OS_READBYTE tests the serial input buffer and returns the character (if any
OS_READBYTE tests the serial input buffer and returns the character (if any
in the A register), the c flag states whether there was a character
in the A register), the c flag states whether there was a character
returned.
returned.


<nowiki> Call : OS_READBYTE
{|class="calltable"
Entry : A, X, Y, c, z = undefined
|Entry||
Exit : X, Y preserved
A, X, Y, c, z = undefined
z = undefined
|-
if c = 1 (no character received)
|Exit||
A is preserved
* X, Y preserved
if c = 0 (character received)
* z = undefined
A = character received</nowiki>
* if c = 1 (no character received)
*: A is preserved
* if c = 0 (character received)
*: A = character received
|}
 
==== OS_SENDBYTE ====


OS_SENDBYTE sends the character in the A register out of the serial
OS_SENDBYTE sends the character in the A register out of the serial
port, taking into consideration flow control etc.
port, taking into consideration flow control etc.


<nowiki> Call : OS_SENDBYTE
{|class="calltable"
Entry : A = character to send
|Entry||
X, Y, c, z = undefined
* A = character to send
Exit : A, X, Y, c is preserved
* X, Y, c, z = undefined
z = undefined</nowiki>
|-
 
|Exit||
* A, X, Y, c is preserved
* z = undefined
|}


=== OS_READJOB and OS_SENDJOB ===
=== OS_READJOB and OS_SENDJOB ===
Line 218: Line 230:
stop.
stop.


<nowiki> Call : OS_READJOB
==== OS_READJOB: Read job value from the user ====
Purpose: To read job value from the user
Entry : A, X, Y, c, z = undefined
Exit : A = byte read
X, Y is preserved
c, z = undefined
Note  : 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 "your own thing", then
OS_READBYTE should be used.  For internal calls this reads data out of
jobin_buf.</nowiki>


<nowiki> Call : OS_SENDJOB
{|class="calltable"
Purpose: To give a job value back to the user
|Entry||
Entry : A = byte to give
* A, X, Y, c, z = undefined
X, Y, c, z = undefined
|-
Exit : A, X, Y is preserved
|Exit||
c, z = undefined
* A = byte read
  Note   : This will send either out to the serial port or place a byte in
* X, Y is preserved
jobout_buf.  For internal calls this stores the value in jobout_buf, which
* c, z = undefined
is allocated 128 bytes, no bound checking is done so sending more than 128
|-
bytes to OS_SENDJOB will cause corruption of some memory.</nowiki>
|Note||
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 "your own thing", then OS_READBYTE should be used.  For internal calls this reads data out of jobin_buf.
|}


==== OS_SENDJOB: Give a job value back to the user ====
{|class="calltable"
|Entry||
* A = byte to give
* X, Y, c, z = undefined
|-
|Exit||
* A, X, Y is preserved
* c, z = undefined
|-
|Note||
This will send either out to the serial port or place a byte in jobout_buf.
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.
|}


=== OS_DECODEJOB ===
=== OS_DECODEJOB ===
Line 265: Line 284:
So for OS_DECODEJOB:
So for OS_DECODEJOB:


<nowiki> Call : OS_DECODEJOB
{|class="calltable"
Entry : A = JobCode
|Entry||
X, Y, c, z = undefined
* A = JobCode
jobin_buf should contain any input data
* X, Y, c, z = undefined
Exit : A = flag
* jobin_buf should contain any input data
X = number of bytes given back by routine
|-
Y = undefined
|Exit||
c = status, if set JobCall does not exist
* A = flag
z = status, if set JobCall cannot be called internally
* X = number of bytes given back by routine
jobout_buf contains any data sent from routine</nowiki>
* Y = undefined
* c = status, if set JobCall does not exist
* z = status, if set JobCall cannot be called internally
* jobout_buf contains any data sent from routine
|}


A typical example would be:
A typical example would be:
Line 297: Line 320:
placed any in the jobout_buf (it would be lost if you had done).
placed any in the jobout_buf (it would be lost if you had done).


=== CallOS ===
=== OS_CALLOS ===


This OS call does various calls which are completely linked to the
This OS call does various calls which are completely linked to the
Line 312: Line 335:
function is not supported.
function is not supported.


==== CALLOS : 0 ====
==== OS_CALLOS 0: Return the OS version number ====
Purpose: Returns the OS version number
 
<nowiki> Entry : A = 0
{|class="calltable"
X, Y, c, z = undefined
|Entry||
Exit : A = Hardware release version
* A = 0
XY = OS version number
* X, Y, c, z = undefined
c, z is preserved
|-
Note : Hardware version 0 is obsolete
|Exit||
  Hardware version 1 is SmartBox
* A = Hardware release version
  VIA at &8030
* XY = OS version number
  UART at &8010
* c, z is preserved
  ADC at &8000
|-
  AUX_PORT (inputs) at &8020 </nowiki>
|Note||
* Hardware version 0 is obsolete
* Hardware version 1 is SmartBox
** VIA at &8030
** UART at &8010
** ADC at &8000
** AUX_PORT (inputs) at &8020
|}
 
==== OS_CALLOS 1: Claim a block of JobCodes ====


==== CALLOS : 1 ====
{|class="calltable"
Purpose: To claim a block of JobCodes
|Entry||
<nowiki> Entry : A = 1
* A = 1
X = number of JobCodes required
* X = number of JobCodes required
Y, c, z = undefined
* Y, c, z = undefined
Exit : A = 0
|-
X = base JobCode, if 0, no big enough block available
|Exit||
Y = Job ID number, 0 if no big enough block
* A = 0
c = set if no big enough block
* X = base JobCode, if 0, no big enough block available
z is preserved
* Y = Job ID number, 0 if no big enough block
Note : This call claims JobCodes codes from the JobCode list.  JobCodes
* c = set if no big enough block
* z is preserved
|-
|Note||
This call claims JobCodes codes from the JobCode list.  JobCodes
are in the range 0 to 255, 0 is special and is the JobCall "Blank".  All the
are in the range 0 to 255, 0 is special and is the JobCall "Blank".  All the
OS ones are spaced out but an "application" can claim a block of JobCodes,
OS ones are spaced out but an "application" can claim a block of JobCodes,
Line 345: Line 381:
maximum of 255 successful calls (not important as their is not that many
maximum of 255 successful calls (not important as their is not that many
free calls available !), the Job ID can be used to check whether a JobCall
free calls available !), the Job ID can be used to check whether a JobCall
request is yours.</nowiki>
request is yours.
|}
 
==== OS_CALLOS 2: Handle NameCode request for the application ====


==== CALLOS : 2 ====
{|class="calltable"
Purpose: To handle NameCode request for the application.
|Entry||
<nowiki> Entry : A = 2
* A = 2
XY = block pointing to block of JobNames
* XY = block pointing to block of JobNames
c, z = undefined
* c, z = undefined
jobin_buf contains JobName (terminated by CR)
* jobin_buf contains JobName (terminated by CR)
zero_gp3 = JobId (as returned by CALLOS 1)
* zero_gp3 = JobId (as returned by CALLOS 1)
Exit : A = 0
|-
XY = undefined
|Exit||
z is preserved
* A = 0
if c = 1 (Name not found)
* XY = undefined
if c = 0 (Name found)
* z is preserved
jobin_buf contains the JobCode
* if c = 1 (Name not found)
Note : Used via the internal_vec to convert from JobName to job code, the
* if c = 0 (Name found)
* jobin_buf contains the JobCode
|-
|Note||
Used via the internal_vec to convert from JobName to job code, the
table format is just a list of the JobNames (each one CR terminated)
table format is just a list of the JobNames (each one CR terminated)
terminated by a &ff</nowiki>
terminated by a &FF
|}
 
==== OS_CALLOS 3: Handle CodeName request for the application ====
 
{|class="calltable"
|Entry||
* A = 3
* XY = block pointing to block of JobNames
* c, z = undefined
* jobin_buf contains JobCode
* zero_gp3 = job id (as returned by CALLOS 1)
|-
|Exit||
* A = 0
* XY = undefined
* z is preserved
* if c = 1 (JobCode not found)
* if c = 0 (JobCode found)
*: jobin contains JobName (terminated by CR)
|-
|Note||
See above
|}


==== CALLOS : 3 ====
==== OS_CALLOS 4: Relocate code ====
Purpose: To handle CodeName request for the application.
<nowiki> Entry : A = 3
XY = block pointing to block of JobNames
c, z = undefined
jobin_buf contains JobCode
zero_gp3 = job id (as returned by CALLOS 1)
Exit : A = 0
XY = undefined
z is preserved
if c = 1 (JobCode not found)
if c = 0 (JobCode found)
jobin contains JobName (terminated by CR)
Note : See above</nowiki>


==== CALLOS : 4 ====
{|class="calltable"
Purpose: To relocate code
|Entry||
<nowiki> Entry : A = 4
* A = 4
XY = execution address (offset from start of code)
* XY = execution address (offset from start of code)
c, z = undefined
* c, z = undefined
zero_gp1 = offset address of execution after relocation
* zero_gp1 = offset address of execution after relocation
zero_gp2 = offset address of relocation BitMap
* zero_gp2 = offset address of relocation BitMap
zero_gp3 = length of code to relocate
* zero_gp3 = length of code to relocate
Exit : No exit, calls begining of code straight away
|-
Note : See later section on Relocation</nowiki>
|Exit||
No exit, calls begining of code straight away
|-
|Note||
See later section on Relocation
|}


==== CALLOS : 5 ====
==== OS_CALLOS 5: Describe the enviroment ====
Purpose: To "describe" the enviroment
 
<nowiki> Entry : A = 5
{|class="calltable"
X, Y, c, z = undefined
|Entry||
Exit : A = 0
* A = 5
c, z is preserved
* X, Y, c, z = undefined
XY = address of table, format as such:
|-
1st byte: number of bytes (ie. 14 currently)
|Exit||
byte 2+:
* A = 0
address of VIA
* c, z is preserved
address of ACIA
* XY = address of table, format as such:
address of ADC
* 1st byte: number of bytes (ie. 14 currently)
address of AUX_PORT
* byte 2+:
address of jobs_status
** address of VIA
address of jobin_buf
** address of ACIA
address of jobout_buf
** address of ADC
Note : It is preferred that this call is consulted first if direct use of
** address of AUX_PORT
the hardware is going to be used to get the correct addresses, or use CALLOS
** address of jobs_status
** address of jobin_buf
** address of jobout_buf
|-
|Note||
It is preferred that this call is consulted first if direct use of
the hardware is going to be used to get the correct addresses, or use OS_CALLOS
0 and decide what addresses/configuration to use from the Hardware version
0 and decide what addresses/configuration to use from the Hardware version
number.</nowiki>
number.
|}


==== CALLOS : 6 ====
==== OS_CALLOS 6: Read a register in the VIA ====
Purpose: To read a register in the VIA
 
<nowiki> Entry : A = 6
{|class="calltable"
X = register (0 to 15)
|Entry||
Y, c, z = undefined
* A = 6
Exit : A = 0
* X = register (0 to 15)
Y = value read
* Y, c, z = undefined
X, c, z is preserved
|-
Note : The register number is not checked for range.  This call allows
|Exit||
* A = 0
* Y = value read
* X, c, z is preserved
|-
|Note||
The register number is not checked for range.  This call allows
access to the hardware without actually directly accessing the hardware
access to the hardware without actually directly accessing the hardware
yourself.</nowiki>
yourself.
|}


==== CALLOS : 7 ====
==== OS_CALLOS 7: Write to a register in the VIA ====
Purpose: To write to a register in the VIA
<nowiki> Entry : A = 7
X = register (0 to 15)
Y = value to write
c, z = undefined
Exit : A = 0
X, Y, c, z is preserved
Note : See above</nowiki>


==== CALLOS : 8 ====
{|class="calltable"
Purpose: To read a register in the ACIA
|Entry||
<nowiki> Entry : A = 8
* A = 7
X = register (0 to 15)
* X = register (0 to 15)
Y, c, z = undefined
* Y = value to write
Exit : A = 0
* c, z = undefined
Y = value read
|-
X, c, z is preserved
|Exit||
Note : See above</nowiki>
* A = 0
* X, Y, c, z is preserved
|-
|Note||
See above
|}


==== CALLOS : 9 ====
==== OS_CALLOS 8: Read a register in the ACIA ====
Purpose: To write to a register in the ACIA
<nowiki> Entry : A = 9
X = register (0 to 15)
Y = value to write
c, z = undefined
Exit : A = 0
X, Y, c, z is preserved
Note : See above</nowiki>


==== CALLOS : 10 ====
{|class="calltable"
Purpose: To read a register in the ADC
|Entry||
<nowiki> Entry : A = 10
* A = 8
X = register (0 to 15)
* X = register (0 to 15)
Y, c, z = undefined
* Y, c, z = undefined
Exit : A = 0
|-
Y = value read
|Exit||
X, c, z is preserved
* A = 0
Note : See above</nowiki>
* Y = value read
* X, c, z is preserved
|-
|Note||
See above
|}


==== CALLOS : 11 ====
==== OS_CALLOS 9: Write to a register in the ACIA ====
Purpose: To write to a register in the ADC
<nowiki> Entry : A = 11
X = register (0 to 15)
Y = value to write
c, z = undefined
Exit : A = 0
X, Y, c, z is preserved
Note : See above</nowiki>


==== CALLOS : 12 ====
Purpose: To read a ADC channel
<nowiki> Entry : A = 12
X = channel number to read (0 to 3)
Y, c, z = undefined
Exit : A = reserved for future use
if c = 0 (8 bit reading)
Y = reading
X = undefined
if c = 1 (16 bit reading)
X = reading (LSB)
Y = reading (MSB)
z = undefined
Note : The A register MAY not return 0, it is guaranteed though that it
will not return 12.</nowiki>


==== CALLOS : 13 ====
{|class="calltable"
Purpose: To read a register in the AUX_PORT
|Entry||
<nowiki> Entry : A = 13
* A = 9
X = register (0 to 15)
* X = register (0 to 15)
Y, c, z = undefined
* Y = value to write
Exit : A = 0
* c, z = undefined
Y = value read
|-
X, c, z is preserved
|Exit||
Note : See above</nowiki>
* A = 0
* X, Y, c, z is preserved
|-
|Note||
See above
|}


==== CALLOS : 14 (OS 2.066+) ====
==== OS_CALLOS 10: Read a register in the ADC ====
Purpose: To start sensor type checking
<nowiki> Entry : A = 14
X, Y, c, z = undefined
Exit : A = 1
Y = value read
X, c, z is preserved
Note : A returns 1.  This flags the ADC routines to start checking the
sensors.  After calling this routine, you should poll CALLOS 15 to check
when all the sensors have been checked.</nowiki>


==== CALLOS : 15 (OS 2.066+) ====
Purpose: To check the status of sensor checking
<nowiki> Entry : A = 15
X, Y, c, z = undefined
Exit : A = status
XY = pointer to sensor types block
c, z is preserved
Note : A does not return 0, though it is guaranteed to not return 15.
  Status of 0 = checking finished and no futher check pending
  Status of 1 = Waiting for current ADC channel to finish converting
before switching sensor checking on
  Status of >127 = Checking sensor types
  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</nowiki>


==== CALLOS : 16 (OS 2.069+) ====
{|class="calltable"
Purpose: To set the OS' irq mask for the VIA
|Entry||
<nowiki> Entry : A = 16
* A = 10
X = EOR mask
* X = register (0 to 15)
Y = AND mask
* Y, c, z = undefined
c, z = undefined
|-
Exit : A = 0
|Exit||
X = old mask value
* A = 0
Y = new mask value
* Y = value read
c, z is preserved
* X, c, z is preserved
Note :</nowiki>
|-
|Note||
See above
|}


==== CALLOS : 17 (OS 2.069+) ====
==== OS_CALLOS 11: Write to a register in the ADC ====
Purpose: To set the OS' irq mask for the ACIA
<nowiki> Entry : A = 17
X = EOR mask
Y = AND mask
c, z = undefined
Exit : A = 0
X = old mask value
Y = new mask value
c, z is preserved
Note :</nowiki>


==== CALLOS : 18 (OS 2.069+) ====
{|class="calltable"
Purpose: To set the OS' irq mask for the ADC
|Entry||
<nowiki> Entry : A = 18
* A = 11
X = EOR mask
* X = register (0 to 15)
Y = AND mask
* Y = value to write
c, z = undefined
* c, z = undefined
Exit : A = 0
|-
X = old mask value
|Exit||
Y = new mask value
* A = 0
c, z is preserved
* X, Y, c, z is preserved
Note :</nowiki>
|-
|Note||
See above
|}


==== CALLOS : 19 (OS 2.069+) ====
==== OS_CALLOS 12: Read an ADC channel ====
Purpose: To set the ACIA's ctrl register and OS' soft copy
<nowiki> Entry : A = 19
X = EOR mask
Y = AND mask
c, z = undefined
Exit : A = 0
X = old mask value
Y = new mask value
c, z is preserved
Note :</nowiki>


==== CALLOS : 20 (OS 2.069+) ====
{|class="calltable"
Purpose: To set the reset vector for battery back RAM support
|Entry||
<nowiki> Entry : A = 20
* A = 12
                XY = address to set reset vector or 0 for read only
* X = channel number to read (0 to 3)
c, z = undefined
* Y, c, z = undefined
Exit : A = 0
|-
XY = old reset vector address
|Exit||
c, z is preserved
* A = reserved for future use
Note : Sets the reset vector to address supplied and sets check bytes in
* if c = 0 (8 bit reading)
workspace.</nowiki>
** Y = reading
** X = undefined
* if c = 1 (16 bit reading)
** X = reading (LSB)
** Y = reading (MSB)
* z = undefined
|-
|Note||
The A register MAY not return 0, it is guaranteed though that it will not return 12.
|}


==== OS_CALLOS 13: Read a register in the AUX_PORT ====


== Vectors ==
{|class="calltable"
|Entry||
* A = 13
* X = register (0 to 15)
* Y, c, z = undefined
|-
|Exit||
* A = 0
* Y = value read
* X, c, z is preserved
|-
|Note||
See above
|}


Some parts of the system are controlled by vectors, vectors also
==== OS_CALLOS 14: Start sensor type checking (OS 2.066+) ====
exist to patch the normal operation of some parts of the system.  Vectors
are RAM pointers, which are called to point to the routine to use.  Being in
RAM this means that they can be altered by the user to allow the user to
modify the operation of part, or all, of a system call.  All the call
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector.
There are vectors which the user patches to pick up unknown jobcodes, and to
service requests like NameCode/CodeName etc.


The vectors are placed at address &200 in memory, each are 2 bytes
{|class="calltable"
long.
|Entry||
* A = 14
* X, Y, c, z = undefined
|-
|Exit||
* A = 1
* Y = value read
* X, c, z is preserved
|-
|Note||
A returns 1.
This flags the ADC routines to start checking the sensors.
After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.
|}


* brk_vec at address &200
==== OS_CALLOS 15: Check the status of sensor checking (OS 2.066+) ====
* nmi_vec at address &202
* irq_vec at address &204
* irq2_vec at address &206
* sendserial_vec at address &208
* readserial_vec at address &20A
* sendjob_vec at address &20C
* readjob_vec at address &20E
* decode_job_vec at address &210
* unknownjob_vec at address &212
* extjob_vec at address &214
* centisec_vec at address &216
* internal_vec at address &218
* callos_vec at address &21A
* printer_vec at address &21C
* reset_vec at address &21E


The brk_vec is called whenever there is a BRK error, which should
{|class="calltable"
not occur under normal use, as the controller OS does not make use of BRK
|Entry||
errors.
* A = 15
* X, Y, c, z = undefined
|-
|Exit||
* A = status
* XY = pointer to sensor types block
* c, z is preserved
|-
|Note||
A does not return 0, though it is guaranteed to not return 15.
* Status of 0 = checking finished and no futher check pending
* Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on
* Status of >127 = Checking sensor types
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
|}


The nmi_vec is called whenever there is a NMI request (not really
==== OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+) ====
applicable, as the NMI line is not connected to anything).


The irq_vec is called whenever there is an interupt.
{|class="calltable"
|Entry||
* A = 16
* X = EOR mask
* Y = AND mask
* c, z = undefined
|-
|Exit||
* A = 0
* X = old mask value
* Y = new mask value
* c, z is preserved
|}


The irq2_vec is called whenever an unknown interupt is encountered,
==== OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+) ====
ie.  didn't come from the controllerXs VIA, ACIA or ADC, or the interupt
from the VIA was not used by the OS.


The sendserial_vec is called when OS_SENDBYTE is called.
{|class="calltable"
|Entry||
* A = 17
* X = EOR mask
* Y = AND mask
* c, z = undefined
|-
|Exit||
* A = 0
* X = old mask value
* Y = new mask value
* c, z is preserved
|}


The readserial_vec is called when OS_READBYTE is called.
==== OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+) ====


The sendjob_vec is called when OS_SENDJOB is called.
{|class="calltable"
|Entry||
* A = 18
* X = EOR mask
* Y = AND mask
* c, z = undefined
|-
|Exit||
* A = 0
* X = old mask value
* Y = new mask value
* c, z is preserved
|}


The readjob_vec is called when OS_READJOB is called.
==== OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+) ====


The decodejob_vec is called when OS_DECODEJOB is called.  Note that
{|class="calltable"
OS_DECODEJOB first calls a OS routine which sets a flag to say it's been
|Entry||
called internally, JobCode requests the OS gets from the serial port are
* A = 19
called via this vector, with a flag saying "from the serial port".
* X = EOR mask
* Y = AND mask
* c, z = undefined
|-
|Exit||
* A = 0
* X = old mask value
* Y = new mask value
* c, z is preserved
|}


The unknownjob_vec is called whenever an unknown JobCode is
==== OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+) ====
encountered.


The extendedjob_vec is called whenever the ExtendedJob is called,
{|class="calltable"
the A register holds the extension value.
|Entry||
* A = 20
* XY = address to set reset vector or 0 for read only
* c, z = undefined
|-
|Exit||
* A = 0
* XY = old reset vector address
* c, z is preserved
|-
|Note||
Sets the reset vector to address supplied and sets check bytes in
workspace.
|}


The centisecond_vec is called 100 times a second from the IRQ
=== OS_PRINTER: Place a character in the printer buffer ===
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.


The internal_vec is called when some information is needed from
{|class="calltable"
various parts of the system, which includes the OS whch lies on the end of
|Entry||
this vector. An example is NameCode, which calls this vector to ask
* A = character to send
everybody if they recognise the JobName in question.
* X, Y, c, z = undefined
|-
|Exit||
* A, X, Y is preserved
* if c = 0 (printed)
*: The printer is on and the character was inserted
* if c = 1 (not printed)
*: The printer is off and the character was forgotten
* z = undefined
|-
|Note||
The character goes into the printer buffer and will be
actually sent to the printer when the printer asks for it. If the printer
was not awake to begin with, an attempt is made to wake it up, thus
meaning that in some circumstances when the printer wasn't ready a
character may be lost, this cannot be helped. If the printer is off (via
the BCD switch) then the character will be forgotten as the Printer Buffer
is inactive.
|}


The callos_vec is called when OS_CALLOS is called.
== Vectors ==


The printer_vec is called when OS_PRINTER is called.
Some parts of the system are controlled by vectors, vectors also
exist to patch the normal operation of some parts of the system.  Vectors
are RAM pointers, which are called to point to the routine to use.  Being in
RAM this means that they can be altered by the user to allow the user to
modify the operation of part, or all, of a system call.  All the call
routines (eg.  OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector.
There are vectors which the user patches to pick up unknown jobcodes, and to
service requests like NameCode/CodeName etc.
 
The vectors are placed at address &200 in memory, each are 2 bytes
long.


The reset_vec is called when the OS gets a reset and the internal
{|class="wikitable"
check bytes flag the integrity of the RAM.  It is first called with C
| brk_vec || &200
cleared for everything to setup vectors and then called with C set for a
| 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.
foreground "language" application to start up.  Use CALLOS 20 to set this
|-
vector.
| nmi_vec || &202
| Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).
|-
| irq_vec || &204
| Called whenever there is an interupt.
|-
| irq2_vec || &206
| 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.
|-
| sendserial_vec || &208
| Called when OS_SENDBYTE is called.
|-
| readserial_vec || &20A
| Called when OS_READBYTE is called.
|-
| sendjob_vec || &20C
| Called when OS_SENDJOB is called.
|-
| readjob_vec || &20E
| Called when OS_READJOB is called.
|-
| decode_job_vec || &210
| 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 "from the serial port".
|-
| unknownjob_vec || &212
| Called whenever an unknown JobCode is encountered.
|-
| extjob_vec || &214
| Called whenever the ExtendedJob is called, the A register holds the extension value.
|-
| centisec_vec || &216
| 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.
|-
| internal_vec || &218
| 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.
|-
| callos_vec || &21A
| Called when OS_CALLOS is called.
|-
| printer_vec || &21C
| Called when OS_PRINTER is called.
|-
| reset_vec || &21E
| 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 "language" application to start up.  Use OS_CALLOS 20 to set this vector.
|}


All vectors should be 'daisy chained', ie.  any routine which uses
All vectors should be 'daisy chained', ie.  any routine which uses
any of them should store the current value in its own workspace and alter
any of them should store the current value in its own workspace and alter
the vector and at the end of the routine which services the vector should
the vector and at the end of the routine which services the vector should
jump to the original saved value, if this isnXt done the system may 'fall'
jump to the original saved value, if this isn't done the system may 'fall'
down.
down.


Line 681: Line 842:
Irqs are reenabled before brk_vec is called
Irqs are reenabled before brk_vec is called


=== Irq_vec ===
=== irq_vec ===


This is called when the 65c02 receives an irq from a device.  As the
This is called when the 65c02 receives an irq from a device.  As the
65c02 signals brk and irq through the same cpu vector the OS first finds out
65c02 signals brk and irq through the same cpu vector the OS first finds out
which one it really was and calls either brk_vec or irq_vec.  It also stores
which one it really was and calls either brk_vec or irq_vec.  It also stores
the A reg at address &a0, as the A reg is corrupted while it finds out which
the A reg at address &A0, as the A reg is corrupted while it finds out which
vector to really call, any irq routine which has claimed the irq should
vector to really call, any irq routine which has claimed the irq should
restore the A reg from &a0 before returning via a RTI, else you should pass
restore the A reg from &A0 before returning via a RTI, else you should pass
on the call to the old vector owner.
on the call to the old vector owner.


Line 694: Line 855:
it can service (or as been masked out) in as-good-as the same conditions as
it can service (or as been masked out) in as-good-as the same conditions as
irq_vec is called.  The OS has a default irq2_vec handler of restoring A
irq_vec is called.  The OS has a default irq2_vec handler of restoring A
from &a0 and doing a RTI.
from &A0 and doing a RTI.


=== Internal_vec and Unknownjob_vec ===
=== internal_vec and unknownjob_vec ===


The main operation of extended JobCalls rely on patching some
The main operation of extended JobCalls rely on patching some
vectors, two infact.  These two are unknownjob_vec and internal_vec.  
vectors, two in fact.  These two are unknownjob_vec and internal_vec.  
unknownjob_vec is the one which the OS calls to actually tell everybody that
unknownjob_vec is the one which the OS calls to actually tell everybody that
a JobCall has been requested which it does not know about (the JobCall MUST
a JobCall has been requested which it does not know about (the JobCall MUST
have been claimed before hand with CALLOS, function 1, or else it is not
have been claimed before hand with OS_CALLOS, function 1, or else it is not
passed on).  internal_vec is the one which the OS calls in request to a
passed on).  internal_vec is the one which the OS calls in request to a
NameCode or CodeName.
NameCode or CodeName.
Line 710: Line 871:
==== internal_vec ====
==== internal_vec ====


===== Function: 0 =====
===== internal_vec 0 =====
Purpose: Nothing
 
<nowiki>  Entry : A = 0
{|class="calltable"
X, Y, c, z = undefined
|Purpose||
Exit : Leave everything as it is and exit straight away
Nothing
Note : Used for claimed calls</nowiki>
|-
|Entry||
* A = 0
* X, Y, c, z = undefined
|-
|Exit||
Leave everything as it is and exit straight away
|-
|Note||
Used for claimed calls
|}
 
===== internal_vec 1 =====
 
{|class="calltable"
|Purpose||
NameCode request
|-
|Entry||
* A = 1
* X, Y, c, z = undefined
* jobin_buf contains JobName (CR terminated)
|-
|Exit||
* if JobName recognised then
** A = 0
** first byte of jobin_buf contains matched JobCode
* if JobName not recognised then
** A = 1
** X, Y, c, z = undefined
|}


===== Function: 1 =====
===== internal_vec 2 =====
Purpose: NameCode request
<nowiki> Entry : A = 1
X, Y, c, z = undefined
jobin_buf contains JobName (CR terminated)
Exit : if JobName recognised then
A = 0
first byte of jobin_buf contains matched JobCode
if JobName not recognised then
A = 1
X, Y, c, z = undefined
Note :</nowiki>


===== Function: 2 =====
{|class="calltable"
Purpose: CodeName request
|Purpose||
<nowiki> Entry : A = 2
CodeName request
X, Y, c, z = undefined
|-
jobin_buf contains JobCode
|Entry||
Exit : if JobCode recognised then
* A = 2
A = 0
* X, Y, c, z = undefined
jobin_buf contains matched JobName (CR terminated)
* jobin_buf contains JobCode
if Code not recognised then
|-
A = 2
|Exit||
X, Y, c, z = undefined
* if JobCode recognised then
Note :</nowiki>
** A = 0
** jobin_buf contains matched JobName (CR terminated)
* if JobCode not recognised then
** A = 2
** X, Y, c, z = undefined
|}


All other calls (ie.  not 0, 1 or 2 should be passed on straight
All other calls (ie.  not 0, 1 or 2 should be passed on straight
Line 747: Line 932:


==== unknownjob_vec ====
==== unknownjob_vec ====
Purpose: To pass on unknown JobCalls to the correct owner
 
<nowiki> Entry : A = enviroment (0 = internal call, 1 = serial call)
{|class="calltable"
Y = JobCode
|Purpose||
X = JOB ID of owner
To pass on unknown JobCalls to the correct owner
c, z = undefined
|-
Exit : if jobcode yours then
|Entry||
if wrong enviroment then
* A = environment (0 = internal call, 1 = serial call)
A = 1
* Y = JobCode
Y = 0
* X = JOB ID of owner
X, c, z = undefined
* c, z = undefined
if right enviroment then
|-
perform function
|Exit||
A = 0
* if jobcode yours then
Y = 0
** if wrong environment then
X, c, z = undefined
*** A = 1
if jobcode not yours then
*** Y = 0
pass on with registers unaltered
*** X, c, z = undefined
Note :</nowiki>
** if right environment then
*** perform function
*** A = 0
*** Y = 0
*** X, c, z = undefined
* if jobcode not yours then
** pass on with registers unaltered
|}


== Starting Up ==
== Starting Up ==
Line 987: Line 1,179:
and not needed again.
and not needed again.


== Other miscellaneous calls ==
== Modules ==
 
<nowiki>Call : OS_PRINTER
Purpose : To place a character in the printer buffer
Entry : A = character to send
  X, Y, c, z = undefined
Exit : A, X, Y is preserved
  if c = 0 (printed)
The printer is on and the character was inserted
  if c = 1 (not printed)
The printer is off and the character was forgotten
  z = undefined
Note : The character goes into the printer buffer and will be
actually sent to the printer when the printer asks for it. If the printer
was not awake to begin with, an attempt is made to wake it up, thus
meaning that in some circumstances when the printer wasn't ready a
character may be lost, this cannot be helped. If the printer is off (via
the BCD switch) then the character will be forgotten as the Printer Buffer
is inactive.</nowiki>


The module is located at &8000 in memory, and should be a EPROM placed
The module is located at &8000 in memory, and should be a EPROM placed
Line 1,020: Line 1,194:
&8000):
&8000):


<nowiki>Offset value comment
{|class="wikitable"
0 "Module" The OS checks for this string
!Offset!!Value!!Comment
6 0 End of check string
|-
7 Language entry Address of 'Language' entry
|0||"Module"||The OS checks for this string
9 Service entry Address of 'Service' entry
|-
11 "<Module Title>" Module title
|6||0||End of check string
0 End of Module title
|-
 
|7||Language entry||Address of 'Language' entry
"<Module part>" Part Module Title
|-
0 End of Part Module Title
|9||Service entry||Address of 'Service' entry
"x.xx" Part version number
|-
 
|11||"<Module Title>"||Module title
&FF End of parts list</nowiki>
|-
| ||0||End of Module title
|-
| ||"<Module part>"||Part Module Title
|-
| ||0||End of Part Module Title
|-
| ||"x.xx"||Part version number
|-
| ||&FF||End of parts list
|}


The language entry is a 2 byte value holding the address of the language
The language entry is a 2 byte value holding the address of the language
Line 1,041: Line 1,225:


The service entry is a 2 byte value holding the address of the service
The service entry is a 2 byte value holding the address of the service
entry routine, this is called at various appropiate moments with service
entry routine, this is called at various appropriate moments with service
numbers in A. A list of current calls is thus:
numbers in A. A list of current calls is thus:


<nowiki>   0 - Not used
* 0 - Not used
  1 - Unknown Job Code
* 1 - Unknown Job Code
  2 - Centisecond call
* 2 - Centisecond call
  3 - Irq2 (unknown IRQ)
* 3 - irq2 (unknown IRQ)
  4 - internal_vec call (exit with A=0 to claim)
* 4 - internal_vec call (exit with A=0 to claim)
254 - RESET
* 254 - RESET
255 - BRK</nowiki>
* 255 - BRK


On exit all registers and flags should be preserved. For call 1 the
On exit all registers and flags should be preserved. For call 1 the
unknown job value is held in the Y register, the enviroment is held in the
unknown job value is held in the Y register, the environment is held in the
X register, on exit, X should contain the flag, as A does from the exit of
X register, on exit, X should contain the flag, as A does from the exit of
unknownjob_vec. For the rest of calls, X and Y are undefined.
unknownjob_vec. For the rest of calls, X and Y are undefined.
Line 1,059: Line 1,243:
For both Service and Language entry points, a value of 0 means "there is
For both Service and Language entry points, a value of 0 means "there is
no valid routine" and the module isn't called for that type of entry.
no valid routine" and the module isn't called for that type of entry.
== Example JobCall ==


Finally a full implementation of a JobCall, from start to finish.
Finally a full implementation of a JobCall, from start to finish.


  <nowiki> DIM data% &1000
  <nowiki>DIM data% &1000
:
:
no_of_calls = 1
no_of_calls = 1
:
:
VIA = &E030
VIA = &E030
ACIA = &E010
ACIA = &E010
ADC = &E000
ADC = &E000
AUX_PORT = &E020
AUX_PORT = &E020
brk_vec = &200
brk_vec = &200
nmi_vec = &202
nmi_vec = &202
irq_vec = &204
irq_vec = &204
irq2_vec = &206
irq2_vec = &206
sendserial_vec = &208
sendserial_vec = &208
readserial_vec = &20A
readserial_vec = &20A
sendjob_vec = &20C
sendjob_vec = &20C
readjob_vec = &20E
readjob_vec = &20E
decodejob_vec = &210
decodejob_vec = &210
unknownjob_vec = &212
unknownjob_vec = &212
extjob_vec = &214
extjob_vec = &214
centisec_vec = &216
centisec_vec = &216
internal_vec = &218
internal_vec = &218
callos_vec = &21A
callos_vec = &21A
printer_vec = &21C
printer_vec = &21C
zero_gp1 = 0
zero_gp1 = 0
zero_gp2 = 2
zero_gp2 = 2
zero_gp3 = 4
zero_gp3 = 4
zero_gp4 = 6
zero_gp4 = 6
zero_gp5 = 8
zero_gp5 = 8
zero_gp6 = 10
zero_gp6 = 10
zero_gp7 = 12
zero_gp7 = 12
zero_gp8 = 14
zero_gp8 = 14
zero_gp9 = 16
zero_gp9 = 16
zero_gp10 = 18
zero_gp10 = 18
user_reserved = &70
user_reserved = &70
irq_A = &A0
irq_A = &A0
fcount = &A1
fcount = &A1
RAM_size = &A3
RAM_size = &A3
jobout_buf = &400
jobout_buf = &400
jobin_buf = &480
jobin_buf = &480
OS_PRINTER = &FFB9
OS_PRINTER = &FFB9
OS_CALLOS = &FFBC
OS_CALLOS = &FFBC
OS_SENDBYTE = &FFBF
OS_SENDBYTE = &FFBF
OS_READBYTE = &FFC2
OS_READBYTE = &FFC2
OS_SENDJOB = &FFC5
OS_SENDJOB = &FFC5
OS_READJOB = &FFC8
OS_READJOB = &FFC8
OS_DECODEJOB = &FFCB
OS_DECODEJOB = &FFCB
:
:
FOR create=1 TO 2
FOR create=1 TO 2
:
  :
FOR pass%=4 TO 7 STEP 3
  FOR pass%=4 TO 7 STEP 3
P%=&100*create : O%=data%
    P%=&100*create : O%=data%
[OPT pass%
    [OPT pass%
:
    :
.job_DemoJob
    .job_DemoJob
CMP #1
    CMP #1
BEQ DemoJob_go
    BEQ DemoJob_go
LDA #1
    LDA #1
LDY #0
    LDY #0
RTS
    RTS
/
    /
.DemoJob_go
    .DemoJob_go








:
    :
.internal_handle
    .internal_handle
CMP #1 ; Is it function call 1 (NameCode) ?
    CMP #1; Is it function call 1 (NameCode) ?
BNE internal_handle_2 ; No, check for function 2
    BNE internal_handle_2; No, check for function 2
/
    /
LDA job_id ; Get our JOB ID number
    LDA job_id; Get our JOB ID number
STA zero_gp3 ; Place it in zero_gp3 for CALLOS
    STA zero_gp3; Place it in zero_gp3 for CALLOS
LDX #job_names MOD 256 ; Our Job name table (LSB)
    LDX #job_names MOD 256; Our Job name table (LSB)
LDY #job_names DIV 256 ; Our Job name table (MSB)
    LDY #job_names DIV 256; Our Job name table (MSB)
LDA #2 ; CALLOS function 2
    LDA #2; CALLOS function 2
JSR OS_CALLOS ; Call CALLOS
    JSR OS_CALLOS; Call CALLOS
BCC internal_yes ; Found, go off and claim call
    BCC internal_yes; Found, go off and claim call
LDA #1 ; Not found, restore A
    LDA #1; Not found, restore A
JMP (ovec) ; Call back to old vector
    JMP (ovec); Call back to old vector
/
    /
.internal_handle_2
    .internal_handle_2
CMP #2 ; Is it function call 2 (CodeName) ?
    CMP #2; Is it function call 2 (CodeName) ?
BNE internal_handle_3 ; No, unknown, pass on
    BNE internal_handle_3; No, unknown, pass on
/
    /
LDA job_id ; Get our JOB ID
    LDA job_id; Get our JOB ID
STA zero_gp3 ; Place it in zero_gp3 for CALLOS
    STA zero_gp3; Place it in zero_gp3 for CALLOS
LDX #job_names MOD 256 ; Our Job name table (LSB)
    LDX #job_names MOD 256; Our Job name table (LSB)
LDY #job_names DIV 256 ; Our Job name table (MSB)
    LDY #job_names DIV 256; Our Job name table (MSB)
LDA #3 ; CALLOS function 3
    LDA #3; CALLOS function 3
JSR OS_CALLOS ; Call CALLOS
    JSR OS_CALLOS; Call CALLOS
BCC internal_yes ; Found, go off and claim call
    BCC internal_yes; Found, go off and claim call
LDA #2 ; Not found, restore A
    LDA #2; Not found, restore A
JMP (ovec) ; Call back to old vector
    JMP (ovec); Call back to old vector
/
    /
.internal_yes
    .internal_yes
LDA #0 ; We want to claim call for some reason
    LDA #0; We want to claim call for some reason
/
    /
.internal_handle_3
    .internal_handle_3
JMP (ovec) ; Call back to old vector
    JMP (ovec); Call back to old vector
:
    :
.job_handle
    .job_handle
PHA ; Make sure to preserve A
    PHA; Make sure to preserve A
CPY call ; Check call wanted against base of ours
    CPY call; Check call wanted against base of ours
BCC job_handle_no ; Less than our base, so definately not ours
    BCC job_handle_no; Less than our base, so definately not ours
TYA
    TYA
SBC #no_of_calls ; Subtract number of calls we have
    SBC #no_of_calls; Subtract number of calls we have
CMP call ; Now compare with base
    CMP call; Now compare with base
BCC job_handle2 ; Yes, one of ours
    BCC job_handle2; Yes, one of ours
/
    /
.job_handle.no
    .job_handle.no
PLA ; Restore A
    PLA; Restore A
JMP (ovec2) ; Call back to old vector
    JMP (ovec2); Call back to old vector
/
    /
.job_handle2
    .job_handle2
TYA
    TYA
SEC
    SEC
SBC call ; Subtract our base from call
    SBC call; Subtract our base from call
ASL A ; Times by 2 for offset into table
    ASL A; Times by 2 for offset into table
TAY ; Place in Y
    TAY; Place in Y
LDA job_run_table,Y ; Get LSB of routine
    LDA job_run_table,Y; Get LSB of routine
STA zero_gp1 ; Store it for indirect jump (LSB)
    STA zero_gp1; Store it for indirect jump (LSB)
LDA job_run_table+1,Y ; Get MSB of routine
    LDA job_run_table+1,Y; Get MSB of routine
STA zero_gp1+1 ; Store it for indirect jump (MSB)
    STA zero_gp1+1; Store it for indirect jump (MSB)
PLA ; Restore A
    PLA; Restore A
JMP (zero_gp1) ; Call our relevant routine
    JMP (zero_gp1); Call our relevant routine
:
    :
.job_names ; List of our JobNames
    .job_names; List of our JobNames
EQUS "DemoJob":EQUB 13 ; Test job name
    EQUS "DemoJob":EQUB 13; Test job name
/
    /
EQUB &FF ; &FF - marks end of table
    EQUB &FF; &FF - marks end of table
:
    :
.job_run_table ; List of routine addresses to match Jobs
    .job_run_table; List of routine addresses to match Jobs
EQUW job_DemoJob ; Test job routine
    EQUW job_DemoJob; Test job routine
:
    :
.call  : EQUB 0 ; Store for our JobCode base
    .call  : EQUB 0; Store for our JobCode base
.job_id : EQUB 0 ; Store for our JobId
    .job_id : EQUB 0; Store for our JobId
.ovec  : EQUW 0 ; Store for old internal_vec value
    .ovec  : EQUW 0; Store for old internal_vec value
.ovec2  : EQUW 0 ; Store for old unknownjob_vec value
    .ovec2  : EQUW 0; Store for old unknownjob_vec value
:
    :
EQUS STRING$(&100-(P% MOD 256),CHR$0)
    EQUS STRING$(&100-(P% MOD 256),CHR$0)
.end_of_code ; end_of_code should be on page boundry
    .end_of_code; end_of_code should be on page boundry
:
    :
.execute
    .execute
LDX #no_of_calls ; Number of JobCodes wanted
    LDX #no_of_calls; Number of JobCodes wanted
LDA #1 ; CALLOS, function 1, Claim JobCodes
    LDA #1; CALLOS, function 1, Claim JobCodes
JSR OS_CALLOS ; Call CALLOS
    JSR OS_CALLOS; Call CALLOS
STX call ; Store base address of JobCodes allocated
    STX call; Store base address of JobCodes allocated
STY job_id ; Store JOB ID given
    STY job_id; Store JOB ID given
CPX #0 ; Check we were given some codes
    CPX #0; Check we were given some codes
BEQ noinstall ; No, don't bother installing ourself
    BEQ noinstall; No, don't bother installing ourself
/
    /
LDA internal_vec ; Get old internal_vec (LSB)
    LDA internal_vec; Get old internal_vec (LSB)
STA ovec ; Store it
    STA ovec; Store it
LDA internal_vec+1 ; Get old internal_vec (MSB)
    LDA internal_vec+1; Get old internal_vec (MSB)
STA ovec+1 ; Store it
    STA ovec+1; Store it
LDA #internal_handle MOD 256 ; Our internal handler (LSB)
    LDA #internal_handle MOD 256; Our internal handler (LSB)
STA internal_vec ; Place it in the vector (LSB)
    STA internal_vec; Place it in the vector (LSB)
LDA #internal_handle DIV 256 ; Our internal handler (MSB)
    LDA #internal_handle DIV 256; Our internal handler (MSB)
STA internal_vec+1 ; Place it in the vector (MSB)
    STA internal_vec+1; Place it in the vector (MSB)
/
    /
LDA unknownjob_vec ; Get old unknownjob_vec (LSB)
    LDA unknownjob_vec; Get old unknownjob_vec (LSB)
STA ovec2 ; Store it
    STA ovec2; Store it
LDA unknownjob_vec+1 ; Get old unknownjob_vec (MSB)
    LDA unknownjob_vec+1; Get old unknownjob_vec (MSB)
STA ovec2+1 ; Store it
    STA ovec2+1; Store it
LDA #job_handle MOD 256 ; Our unknown job handler (LSB)
    LDA #job_handle MOD 256; Our unknown job handler (LSB)
STA unknownjob_vec ; Place it in the vector (LSB)
    STA unknownjob_vec; Place it in the vector (LSB)
LDA #job_handle DIV 256 ; Our unknown job handler (MSB)
    LDA #job_handle DIV 256; Our unknown job handler (MSB)
STA unknownjob_vec+1 ; Place it in the vector (MSB)
    STA unknownjob_vec+1; Place it in the vector (MSB)
/
    /
LDX #end_of_code MOD 256 ; End of code (LSB)
    LDX #end_of_code MOD 256; End of code (LSB)
LDY #end_of_code DIV 256 ; End of code (MSB)
    LDY #end_of_code DIV 256; End of code (MSB)
STX jobin_buf ; Place it in jobin_buf (LSB)
    STX jobin_buf; Place it in jobin_buf (LSB)
STY jobin_buf+1 ; Place it in jobin_buf+1 (MSB)
    STY jobin_buf+1; Place it in jobin_buf+1 (MSB)
LDA #65 ; JobCode for WriteLomem
    LDA #65; JobCode for WriteLomem
JSR OS_DECODEJOB ; Call JobCall handler
    JSR OS_DECODEJOB; Call JobCall handler
/
    /
.noinstall
    .noinstall
RTS ; End - return to OS
    RTS; End - return to OS
:
    :
.execcall
    .execcall
LDA #>(execute-address) ; Final execution address offset (LSB)
    LDA #>(execute-address); Final execution address offset (LSB)
STA zero_gp1
    STA zero_gp1
LDA #<(execute-address) ; Final execution address offset (MSB)
    LDA #<(execute-address); Final execution address offset (MSB)
STA zero_gp1+1
    STA zero_gp1+1
/
    /
LDA #>(BitMap-address) ; BitMap address offset (LSB)
    LDA #>(BitMap-address); BitMap address offset (LSB)
STA zero_gp2
    STA zero_gp2
LDA #<(BitMap-address) ; BitMap address offset (MSB)
    LDA #<(BitMap-address); BitMap address offset (MSB)
STA zero_gp2+1
    STA zero_gp2+1
/
    /
LDA #>(execcall-address) ; Length of data to relocate (LSB)
    LDA #>(execcall-address); Length of data to relocate (LSB)
STA zero_gp3
    STA zero_gp3
LDA #<(execcall-address) ; Length of data to relocate (MSB)
    LDA #<(execcall-address); Length of data to relocate (MSB)
STA zero_gp3+1
    STA zero_gp3+1
/
    /
TXA ; Execcall REAL address
    TXA; Execcall REAL address
SEC
    SEC
SBC #>(execcall-address) ; Subrtract length
    SBC #>(execcall-address); Subtract length
TAX ; Put it back in X
    TAX; Put it back in X
TYA ; Ditto for MSB
    TYA; Ditto for MSB
SBC #<(execcall-address)
    SBC #<(execcall-address)
TAY
    TAY
LDA #4 ; CALLOS function for Relocate
    LDA #4; CALLOS function for Relocate
JMP OS_CALLOS ; Call CALLOS
    JMP OS_CALLOS; Call CALLOS
/
    /
.BitMap ; BitMap start
    .BitMap; BitMap start
:
    :
]
    ]
NEXT
  NEXT
:
  :
OSCLI "SAVE code"+STR$create+" "+STR$~data%+" "+STR$~O%
  OSCLI "SAVE code"+STR$create+" "+STR$~data%+" "+STR$~O%
:
  :
NEXT</nowiki>
NEXT</nowiki>


== Changelog ==
== Changelog ==


<nowiki>bob 071 Fixed MotorForward bug (%10)
{|class="wikitable"
bob 072 CallOS 14 now checks current adc.owner status
|bob||071||Fixed MotorForward bug (%10)
jim 072.d1 * Rewrite for new hardware/processor *
|-
jim 072.d1 Moved vectors/workspace to &440
|bob||072||CallOS 14 now checks current adc.owner status
jim 072.d1 Moved rs.inp.buf to &500
|-
jim 072.d1 Moved jobout.buf to &600
|jim||072.d1||* Rewrite for new hardware/processor *
jim 072.d1 Moved jobin.buf to &680
|-
jim 072.d1 Moved jobs.status to &700
|jim||072.d1||Moved vectors/workspace to &440
jim 072.d1 Added irq.X (&A4)
|-
jim 072.d1 Added irq.Y (&A5)
|jim||072.d1||Moved rs.inp.buf to &500
jim 072.d1 Removed short detection code
|-
jim 072.d1 Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19
|jim||072.d1||Moved jobout.buf to &600
jim 072.d1 Removed JobCalls ReadADCReg,WriteADCReg
|-
jim 072.d1 Removed JobCalls ReadACIAReg,WriteACIAReg
|jim||072.d1||Moved jobin.buf to &680
jim 072.d1 Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow
|-
jim 072.d1 Added CallOS 21 Write Outputs
|jim||072.d1||Moved jobs.status to &700
jim 072.d1 Added CallOS 22 Read Outputs added
|-
jim 072.d1 Added CallOS 23 Read Inputs added
|jim||072.d1||Added irq.X (&A4)
jim 072.d1 Added CallOS 24 Write Motors
|-
jim 072.d1 Added CallOS 25 Read Motors
|jim||072.d1||Added irq.Y (&A5)
jim 072.d1 Added CallOS 26 Read Keypad
|-
jim 072.d1 Expanded CallOS 5 information
|jim||072.d1||Removed short detection code
jim 072.d1 Added JobCall IdentSystem to return CallOS 5 info
|-
jim 072.d1 JobCalls now use appropriate CallOS'
|jim||072.d1||Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19
jim 072.d1 OS.READBYTE no longer preserves A on CS
|-
jim 072.d1 irq.vec,irq2.vec,nmi.vec now redundant
|jim||072.d1||Removed JobCalls ReadADCReg,WriteADCReg
jim 072.d1 Renamed JobCall DownloadData to DownloadData38
|-
jim 072.d1 Renamed JobCall UploadData to UploadData38
|jim||072.d1||Removed JobCalls ReadACIAReg,WriteACIAReg
jim 072.d1 Renamed JobCall ExecuteCode to ExecuteCode38
|-
jim 072.d1 Renamed JobCall ReadByte to ReadByte38
|jim||072.d1||Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow
jim 072.d1 Renamed JobCall StoreByte to StoreByte38
|-
jim 072.d1 JobCall ForcedADCRead now checks adc.owner first
|jim||072.d1||Added CallOS 21 Write Outputs
jim 072.d3 Added CallOS 27 Write printer
|-
jim 072.d3 Added CallOS 28 Read printer
|jim||072.d1||Added CallOS 22 Read Outputs added
jim 072.d3 Added CallOS 29 Write RTC Reg
|-
jim 072.d3 Added CallOS 30 Read RTC Reg
|jim||072.d1||Added CallOS 23 Read Inputs added
jim 072.d3 Added CallOS 31 Write RTC string
|-
jim 072.d3 Added CallOS 32 Read RTC string
|jim||072.d1||Added CallOS 24 Write Motors
jim 072.d3 Added CallOS 33 Write RTC bcd
|-
jim 072.d3 Added CallOS 34 Read RTC bcd
|jim||072.d1||Added CallOS 25 Read Motors
jim 072.d3 Added CallOS 35 Write LCD Reg
|-
jim 072.d3 Added CallOS 36 Read LCD Reg
|jim||072.d1||Added CallOS 26 Read Keypad
jim 072.d3 Added JobCall WritePrinter
|-
jim 072.d3 Added JobCall ReadPrinter
|jim||072.d1||Expanded CallOS 5 information
jim 072.d3 Added JobCall PrintChar
|-
jim 072.d3 Added JobCall PrintStreamZ
|jim||072.d1||Added JobCall IdentSystem to return CallOS 5 info
jim 072.d3 Added JobCall PrintStream
|-
jim 072.d3 Added JobCall PrintServer
|jim||072.d1||JobCalls now use appropriate CallOS'
jim 072.d3 Added JobCall WriteRTCReg
|-
jim 072.d3 Added JobCall ReadRTCReg
|jim||072.d1||OS.READBYTE no longer preserves A on CS
jim 072.d3 Added JobCall WriteRTC
|-
jim 072.d3 Added JobCall ReadRTC
|jim||072.d1||irq.vec,irq2.vec,nmi.vec now redundant
jim 072.d3 Added JobCall WriteRTCbcd
|-
jim 072.d3 Added JobCall ReadRTCbcd
|jim||072.d1||Renamed JobCall DownloadData to DownloadData38
jim 072.d3 Added JobCall WriteLCDReg
|-
jim 072.d3 Added JobCall ReadLCDReg
|jim||072.d1||Renamed JobCall UploadData to UploadData38
jim 072.d3 Added CallOS 37 Write Power ctrl
|-
jim 072.d3 Added CallOS 38 Read Power ctrl
|jim||072.d1||Renamed JobCall ExecuteCode to ExecuteCode38
jim 072.d3 Implemented OS.PRINTER
|-
jim 072.d3 Added JobCall PatchMF (MotorForward)
|jim||072.d1||Renamed JobCall ReadByte to ReadByte38
jim 072.d3 Changed irq.vec to int0.vec
|-
jim 072.d3 Changed irq2.vec to int1.vec
|jim||072.d1||Renamed JobCall StoreByte to StoreByte38
jim 072.d3 Added int2irq.vec (wrksp+&20)
|-
jim 072.d3 Added int3irq.vec (wrksp+&22)
|jim||072.d1||JobCall ForcedADCRead now checks adc.owner first
jim 072.d3 Added int4irq.vec (wrksp+&24)
|-
jim 072.d3 Added c0irq.vec (wrksp+&26)
|jim||072.d3||Added CallOS 27 Write printer
jim 072.d3 Added c1irq.vec (wrksp+&28)
|-
jim 072.d3 Added t1irq.vec (wrksp+&2A)
|jim||072.d3||Added CallOS 28 Read printer
jim 072.d3 Added t2irq.vec (wrksp+&2C)
|-
jim 072.d3 Added txirq.vec (wrksp+&2E)
|jim||072.d3||Added CallOS 29 Write RTC Reg
jim 072.d3 Added tyirq.vec (wrksp+&30)
|-
jim 072.d3 Added s1rirq.vec (wrksp+&32)
|jim||072.d3||Added CallOS 30 Read RTC Reg
jim 072.d3 Added s1rirq.vec (wrksp+&34)
|-
jim 072.d3 Added s2irq.vec (wrksp+&36)
|jim||072.d3||Added CallOS 31 Write RTC string
jim 072.d3 Added adcirq.vec (wrksp+&38)
|-
jim 072.d3 Renamed JobCall DownloadData38 to DownloadData740
|jim||072.d3||Added CallOS 32 Read RTC string
jim 072.d3 Renamed JobCall UploadData38 to UploadData740
|-
jim 072.d3 Renamed JobCall ExecuteCode38 to ExecuteCode740
|jim||072.d3||Added CallOS 33 Write RTC bcd
jim 072.d3 Renamed JobCall ReadByte38 to ReadByte740
|-
jim 072.d3 Renamed JobCall StoreByte38 to StoreByte740
|jim||072.d3||Added CallOS 34 Read RTC bcd
jim 072.d4 Hardware update
|-
jim 072.d4 Re-assigned CallOS 37 to Write Power/Charge control
|jim||072.d3||Added CallOS 35 Write LCD Reg
jim 072.d4 Re-assigned CallOS 38 to Read Power/Charge control
|-
jim 072.d4 Moved int2irq.vec to wrksp+&22
|jim||072.d3||Added CallOS 36 Read LCD Reg
jim 072.d4 Moved int3irq.vec to wrksp+&24
|-
jim 072.d4 Moved int4irq.vec to wrksp+&26
|jim||072.d3||Added JobCall WritePrinter
jim 072.d4 Moved c0irq.vec to wrksp+&28
|-
jim 072.d4 Moved c1irq.vec to wrksp+&2A
|jim||072.d3||Added JobCall ReadPrinter
jim 072.d4 Moved t1irq.vec to wrksp+&2C
|-
jim 072.d4 Moved t2irq.vec to wrksp+&2E
|jim||072.d3||Added JobCall PrintChar
jim 072.d4 Moved txirq.vec to wrksp+&30
|-
jim 072.d4 Moved tyirq.vec to wrksp+&32
|jim||072.d3||Added JobCall PrintStreamZ
jim 072.d4 Moved s1rirq.vec to wrksp+&34
|-
jim 072.d4 Moved s1rirq.vec to wrksp+&36
|jim||072.d3||Added JobCall PrintStream
jim 072.d4 Moved s2irq.vec to wrksp+&38
|-
jim 072.d4 Moved adcirq.vec to wrksp+&3A
|jim||072.d3||Added JobCall PrintServer
jim 072.d4 Added OS.LCDVDU (&FFB7) and lcdvdu.vec (wrksp+&20)
|-
jim 072.d5 Re-assigned CallOS 37 to Write/Read Power/Charge control
|jim||072.d3||Added JobCall WriteRTCReg
jim 072.d5 Re-assgined CallOS 38 Read Battery Voltage
|-
jim 072.d5 OS.CALLOS now re-entrant
|jim||072.d3||Added JobCall ReadRTCReg
jim 072.d5 Fixed OS.PRINTER
|-
jim 072.d5 Started to add internal logging software
|jim||072.d3||Added JobCall WriteRTC
jim 072.d5 Moved OS.LCDVDU to &FFB6
|-
jim 072.d7 Added OS.PRINTERPOLL (&FFB3) and printerpoll.vec (wrksp+&3C)
|jim||072.d3||Added JobCall ReadRTC
bob 073 Matched jim & bob sensor lookup tables
|-
bob 073 Added PatchMF
|jim||072.d3||Added JobCall WriteRTCbcd
jim 072.dj Added OS.CALLOS 51 Read keypad press
|-
jim 072.dj Added hard reset keypad press
|jim||072.d3||Added JobCall ReadRTCbcd
jim 072.dk Added JobCall ReadOutputs
|-
jim 072.do Changed soft reset of zero page locations
|jim||072.d3||Added JobCall WriteLCDReg
jim 072.do Changed MotorForward/Backward bitmaps
|-
jim 072.dp Fixed OS.CALLOS WriteRTCbcd
|jim||072.d3||Added JobCall ReadLCDReg
jim 072.dq Fixed OS.CALLOS ReadRTCstring for correct 24hr operation
|-
jim 072.dr Changed reset prompts
|jim||072.d3||Added CallOS 37 Write Power ctrl
bob 074 Added support for Little Bob
|-
jim 072.dy Added further RTC/CMOS support
|jim||072.d3||Added CallOS 38 Read Power ctrl
jim 072.dB Removed double hard reset
|-
jim 072.dE Set _cpu.mode depending on external memory requirement
|jim||072.d3||Implemented OS.PRINTER
jim 072.dF Added Insight code
|-
jim 073 Sub-release
|jim||072.d3||Added JobCall PatchMF (MotorForward)
jim 074 Modification to LCD code - uses bsy flag all the time now
|-
jim 074 Fixed OS.CALLOS 42 Write LCD Char Def
|jim||072.d3||Changed irq.vec to int0.vec
jim 074 Fixed OS.CALLOS 43 Read LCD Char Def
|-
jim 075 Added simple battery charge code
|jim||072.d3||Changed irq2.vec to int1.vec
jim 075 Fixed JobCall ReadSensorTable
|-
jim 075 Fixed OS.CALLOS 42 Write LCD Char Def
|jim||072.d3||Added int2irq.vec (wrksp+&20)
jim 075 Fixed OS.CALLOS 43 Read LCD Char Def
|-
jim 075 Added sleep code
|jim||072.d3||Added int3irq.vec (wrksp+&22)
jim 075 Added OS.CALLOS 52 Write Sleep Time
|-
jim 075 Added OS.CALLOS 53 Read Sleep Time
|jim||072.d3||Added int4irq.vec (wrksp+&24)
jim 075 Modified battery charge code
|-
jim 076 Modified JobCall ReadSensorTable
|jim||072.d3||Added c0irq.vec (wrksp+&26)
jim 076 Modified OS.CALLOS 26 ReadKeypad to do debounce
|-
jim 077 Rewrote battery charging code
|jim||072.d3||Added c1irq.vec (wrksp+&28)
jim 077.2 Reduced stop charge threshold to 2
|-
jim 077.3 Increased stop charge threshold to 3
|jim||072.d3||Added t1irq.vec (wrksp+&2A)
jim 077.3 Added battery voltage averaging
|-
bill 077 Stripped
|jim||072.d3||Added t2irq.vec (wrksp+&2C)
bill 077 Moved ins to out port
|-
bill 077 Added RTS
|jim||072.d3||Added txirq.vec (wrksp+&2E)
bill 077 Changed sensor ID system back to original, using p2.1
|-
bill 077 Dedicated USB/RS selection line, RTS provisioned on p2.3
|jim||072.d3||Added tyirq.vec (wrksp+&30)
bill 077 Overload "bounce" timing
|-
bill 078 CTS added
|jim||072.d3||Added s1rirq.vec (wrksp+&32)
bill 079 Temperature fudge</nowiki>
|-
|jim||072.d3||Added s1rirq.vec (wrksp+&34)
|-
|jim||072.d3||Added s2irq.vec (wrksp+&36)
|-
|jim||072.d3||Added adcirq.vec (wrksp+&38)
|-
|jim||072.d3||Renamed JobCall DownloadData38 to DownloadData740
|-
|jim||072.d3||Renamed JobCall UploadData38 to UploadData740
|-
|jim||072.d3||Renamed JobCall ExecuteCode38 to ExecuteCode740
|-
|jim||072.d3||Renamed JobCall ReadByte38 to ReadByte740
|-
|jim||072.d3||Renamed JobCall StoreByte38 to StoreByte740
|-
|jim||072.d4||Hardware update
|-
|jim||072.d4||Re-assigned CallOS 37 to Write Power/Charge control
|-
|jim||072.d4||Re-assigned CallOS 38 to Read Power/Charge control
|-
|jim||072.d4||Moved int2irq.vec to wrksp+&22
|-
|jim||072.d4||Moved int3irq.vec to wrksp+&24
|-
|jim||072.d4||Moved int4irq.vec to wrksp+&26
|-
|jim||072.d4||Moved c0irq.vec to wrksp+&28
|-
|jim||072.d4||Moved c1irq.vec to wrksp+&2A
|-
|jim||072.d4||Moved t1irq.vec to wrksp+&2C
|-
|jim||072.d4||Moved t2irq.vec to wrksp+&2E
|-
|jim||072.d4||Moved txirq.vec to wrksp+&30
|-
|jim||072.d4||Moved tyirq.vec to wrksp+&32
|-
|jim||072.d4||Moved s1rirq.vec to wrksp+&34
|-
|jim||072.d4||Moved s1rirq.vec to wrksp+&36
|-
|jim||072.d4||Moved s2irq.vec to wrksp+&38
|-
|jim||072.d4||Moved adcirq.vec to wrksp+&3A
|-
|jim||072.d4||Added OS.LCDVDU (&FFB7) and lcdvdu.vec (wrksp+&20)
|-
|jim||072.d5||Re-assigned CallOS 37 to Write/Read Power/Charge control
|-
|jim||072.d5||Re-assgined CallOS 38 Read Battery Voltage
|-
|jim||072.d5||OS.CALLOS now re-entrant
|-
|jim||072.d5||Fixed OS.PRINTER
|-
|jim||072.d5||Started to add internal logging software
|-
|jim||072.d5||Moved OS.LCDVDU to &FFB6
|-
|jim||072.d7||Added OS.PRINTERPOLL (&FFB3) and printerpoll.vec (wrksp+&3C)
|-
|bob||073||Matched jim & bob sensor lookup tables
|-
|bob||073||Added PatchMF
|-
|jim||072.dj||Added OS.CALLOS 51 Read keypad press
|-
|jim||072.dj||Added hard reset keypad press
|-
|jim||072.dk||Added JobCall ReadOutputs
|-
|jim||072.do||Changed soft reset of zero page locations
|-
|jim||072.do||Changed MotorForward/Backward bitmaps
|-
|jim||072.dp||Fixed OS.CALLOS WriteRTCbcd
|-
|jim||072.dq||Fixed OS.CALLOS ReadRTCstring for correct 24hr operation
|-
|jim||072.dr||Changed reset prompts
|-
|bob||074||Added support for Little Bob
|-
|jim||072.dy||Added further RTC/CMOS support
|-
|jim||072.dB||Removed double hard reset
|-
|jim||072.dE||Set _cpu.mode depending on external memory requirement
|-
|jim||072.dF||Added Insight code
|-
|jim||073||Sub-release
|-
|jim||074||Modification to LCD code - uses bsy flag all the time now
|-
|jim||074||Fixed OS.CALLOS 42 Write LCD Char Def
|-
|jim||074||Fixed OS.CALLOS 43 Read LCD Char Def
|-
|jim||075||Added simple battery charge code
|-
|jim||075||Fixed JobCall ReadSensorTable
|-
|jim||075||Fixed OS.CALLOS 42 Write LCD Char Def
|-
|jim||075||Fixed OS.CALLOS 43 Read LCD Char Def
|-
|jim||075||Added sleep code
|-
|jim||075||Added OS.CALLOS 52 Write Sleep Time
|-
|jim||075||Added OS.CALLOS 53 Read Sleep Time
|-
|jim||075||Modified battery charge code
|-
|jim||076||Modified JobCall ReadSensorTable
|-
|jim||076||Modified OS.CALLOS 26 ReadKeypad to do debounce
|-
|jim||077||Rewrote battery charging code
|-
|jim||077.2||Reduced stop charge threshold to 2
|-
|jim||077.3||Increased stop charge threshold to 3
|-
|jim||077.3||Added battery voltage averaging
|-
|bill||077||Stripped
|-
|bill||077||Moved ins to out port
|-
|bill||077||Added RTS
|-
|bill||077||Changed sensor ID system back to original, using p2.1
|-
|bill||077||Dedicated USB/RS selection line, RTS provisioned on p2.3
|-
|bill||077||Overload "bounce" timing
|-
|bill||078||CTS added
|-
|bill||079||Temperature fudge
|}

Latest revision as of 17:44, 4 November 2023

Documentation for SmartBox OS 2.066[edit]

The term JobCall is the concept of the routine itself, as opposed to JobName (which is the actual name associated with it (eg. NameCode)) and JobCode (which is the actual number associated with it (eg. 3)).

The term controller means the SmartBox, or any other incarnation which may appear in future time !

The term OS means the controllers' Operating System.

Preview[edit]

There are a number of reasons why using Machine Code on the controller is more desirable than using the various JobCalls via the serial link. One reason is speed, if the task which needs to be performed, has to be performed very quickly (more quickly than can be achieved via the serial link at least), then some code must be written to work in the controller itself to achieve this sort of speed. If the task required is to be a "background" task (ie. a task which may happen while other "things" carry on as normal, like a change of state of a particular port) then it is far easier to let the controller do all the work, maybe under interupts, than continuously poll the controller checking the state of various things. Another reason may be that you would like to add another JobCall to the OS, extending the calls available to external applications (and the user at the same time), without the user having to write the code himself to achieve the aim. For advanced users, there is also the ablity to alter the way the OS does various things and to have the controller in their "power", for dedicated tasks etc.

One advantage of writing IN the controller is that that "function" is available to any computer using the serial link, and is thus not fixed to one computer type or even a computer at all, where the controller is used free running, doing a particular task which doesn't need the intervention of the user's own computer.

Call types[edit]

There are three different types of machine code routines which can be added to the controller. Each one is suited to a more advanced level than the other, which also relates to the amount of work needed to actually get the routine working ! The three levels are such:

Simple stand alone routine[edit]

Here the routine is just the routine itself with no supporting code (ie. code to integrate with the rest of the OS). It has to be called directly, by use of the ExecuteCode JobCall, thus it usually resides at a fixed address and obviously does not offer the user friendly way of accessing itself like there is with normal OS calls.

Extended JobCall[edit]

There is one JobCall in the OS (ExtendJob) which can call various routines residing in memory, each one is identified by a 8 bit number. The disadvantages of this way is that the call hasn't got it's own JobName/JobCode and needs the extra byte to identify itself. This is ideal for the quick routine which the user would like to add for himself without having to resort to the extra code needed for the full JobCode implementation.

Full JobCall[edit]

This is where the routine can define itself one (or a number) of JobNames/JobCodes, it can integrate itself into an extension of the OS in a complete way. The disadvantage is that extra coding is needed to deal with the various OS calls it has to service and that extra work is needed to produce a suitable file to place in the controller.

Normally with most processors, including the 65c02, the machine code produced is produced for a fixed memory address, and it cannot be made to run in a different place in memory unless extra work is carried out by the user, or at least a combination of the user and the OS. For the controller this is a very severe disadvantage as most extensions are downloaded as "modules" and to optimise memory a system has to be devised to enable routines to be placed anywhere in memory, thus allowing no memory to be wasted. In the OS there is a routine to do just this, though it does need some help. A special version of the users code has to be developed, or rather it has to be "processed" to add extra information on the end. Extra code is needed to set up a few pointers and call the OS routine to relocate. See later.

Memory[edit]

The first 256 bytes of memory are somewhat special, most zero page locations are reserved for use by the OS:

&00-&14 General workspace, used to pass parameters to some OS routines, can also be used by other routines DURING their execution.
&15-&6F Reserved
&70-&9F Available to the user
&A0 Accumulator store for irqs (irq_A)
&A1-&A2 Two byte counter, decremented at 100hz, useful for temporary timing purposes (fcount)
&A3 RAM size, high byte of RAM size of machine, ie. for 32k this will be &80 (RAM_size)
&A4-&AF Reserved
&B0-&FF Used by the OS

The rest of memory space is used for the processors stack, OS' buffers, tables, vectors and, of course, the user available memory.

A breakdown of the rest of memory:

  • &100 to &1FF is the 65c02's stack.
  • &200-&2FF is used by the OS for various system functions and also contains the vector table, more on that later.
  • &300-&3FF is the serial input buffer.
  • &400-&47F is the internal job code call output buffer. (jobout_buf)
  • &480-&4FF is the internal job code call input buffer. (jobin_buf)
  • &500-&5FF is the job status table.
  • &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.

In the controller the maximum limit of RAM is &8000 (which is 32k), from &8000-&DFFF lies the memory mapped hardware, from &E000 to &FFF9 lies the OS and from &FFFA to &FFFF lies the 65c02 vectors.

So the user has his own limits of free memory. The lowest limit is called LOMEM and the highest limit is called HIMEM, everything between these boundaries is termed "available" for use by anyone, the user or the OS.

Anyone can also claim memory, thus making private to them and not allowing anyone else to use it. You do this by altering the value of LOMEM, HIMEM should not be moved. Of course anyone claiming memory should first check that there is enough memory available for the amount they want to claim for. To take a common example, a downloadable routine which installs itself as a extension routine, The download routine will first of all read LOMEM, read HIMEM, check there is enough memory for the routine, download it to LOMEM and call it, the downloaded routine will then relocate itself and alter LOMEM so it is protected.

There is one other memory limit, this is called TOPMEM, this is the absolute top limit of RAM, HIMEM will normally equal TOPMEM, but a future OS may alter HIMEM to below TOPMEM. TOPMEM should be ignored ideally, and HIMEM used.

OS Calls[edit]

The OS has calls (ie. a jump instruction which you call, which then calls, via a RAM vector, a routine in the OS) setup near the top of memory, through these calls you do everything associated with the controller. The calls are are such:

  • OS_PRINTER at address &FFB9
  • OS_CALLOS at address &FFBC
  • OS_SENDBYTE at address &FFBF
  • OS_READBYTE at address &FFC2
  • OS_SENDJOB at address &FFC5
  • OS_READJOB at address &FFC8
  • OS_DECODEJOB at address &FFCB

The address is the one to call for the particular routine.

  • OS_PRINTER is reserved for future use.
  • OS_CALLOS calls upon the OS to do various things internally.
  • OS_SENDBYTE sends a byte out of the serial port.
  • OS_READBYTE reads a byte from the serial port.
  • OS_SENDJOB 'sends' a value to the JobCall caller.
  • OS_READJOB 'reads' a value from the JobCalls caller.
  • OS_DECODEJOB decodes the Job number held in A and acts on it.

Note none of these things should be used in interupt routines, except OS_CALLOS, of any sort, except if YOU know what state the machine is in and WHY.

OS_READBYTE and OS_SENDBYTE: Serial Port[edit]

The serial port on the controller is currently based on the 6850 UART, this need not bother you as the OS deals with the thing entirely by itself. The chip is setup by the OS to always have a word format of 8n1 and a baud rate of 9600, this translates to 960cps. The 6850 has two divide rates for the baud rate, the OS only uses one, using the other one is beyond the scope of this document and shouldn't be neccessary to use.

Only receive interupts are used, transmission of characters is done on a polled basis.

As said, there are two calls OS_READBYTE and OS_SENDBYTE.

OS_READBYTE[edit]

OS_READBYTE tests the serial input buffer and returns the character (if any in the A register), the c flag states whether there was a character returned.

Entry

A, X, Y, c, z = undefined

Exit
  • X, Y preserved
  • z = undefined
  • if c = 1 (no character received)
    A is preserved
  • if c = 0 (character received)
    A = character received

OS_SENDBYTE[edit]

OS_SENDBYTE sends the character in the A register out of the serial port, taking into consideration flow control etc.

Entry
  • A = character to send
  • X, Y, c, z = undefined
Exit
  • A, X, Y, c is preserved
  • z = undefined

OS_READJOB and OS_SENDJOB[edit]

To send/receive data from the user (either internally or via the serial port) you call OS_SENDJOB/OS_READJOB for the next byte. If your JobCall requires dynamic use of data, ie. interaction, then a check of the enviroment should be done to check the call came from the serial port. One example of this is the OS JobCall MultipleServer, which requires dynamic use of sending a stream of data until a reaction from the user to tell it to stop.

OS_READJOB: Read job value from the user[edit]

Entry
  • A, X, Y, c, z = undefined
Exit
  • A = byte read
  • X, Y is preserved
  • c, z = undefined
Note

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 "your own thing", then OS_READBYTE should be used. For internal calls this reads data out of jobin_buf.

OS_SENDJOB: Give a job value back to the user[edit]

Entry
  • A = byte to give
  • X, Y, c, z = undefined
Exit
  • A, X, Y is preserved
  • c, z = undefined
Note

This will send either out to the serial port or place a byte in jobout_buf. 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.

OS_DECODEJOB[edit]

JobCalls in practise are really only used by the user, JobCalls tend not to call other calls at all, infact you shouldn't really call another call unless you were called from the serial port. If you do need to call another JobCall then you do it like this; with each call you have associated with it a number, and the call might require some data or give some data back or both. With the serial port this is simple, you just send data down and receive data as it is produced. You can't do this with internal calls, and some calls will not let you call them from inside the controller, you HAVE to call them from outside. But for the ones which do let you, you do it with two data blocks, one for input TO the call and one for output FROM the call. These are setup in a position in memory (jobin_buf and jobout_buf), each one has a maximum of 128 bytes and this should not be overflowed (OS_READJOB and OS_SENDJOB do NOT do any bound checking). So to start with you place in the input block the data you want to give the call (if any), call it, and then act on the data in the output block (if any). The OS call OS_DECODEJOB is the one which actually calls the JobCall handler and eventually the actual routine, you tell the JobCall handler which call you want by placing the JobCode in the A register.

So for OS_DECODEJOB:

Entry
  • A = JobCode
  • X, Y, c, z = undefined
  • jobin_buf should contain any input data
Exit
  • A = flag
  • X = number of bytes given back by routine
  • Y = undefined
  • c = status, if set JobCall does not exist
  • z = status, if set JobCall cannot be called internally
  • jobout_buf contains any data sent from routine

A typical example would be:

	LDA #readhimem		; JobCode for ReadHimem
	JSR OS_DECODEJOB	; Call the JobCall handler
	LDX jobout_buf		; Read HIMEM from buffer LSB
	LDY jobout_buf+1	; Read HIMEM from buffer MSB
	STX jobin_buf		; Place LSB in input buffer
	STY jobin_buf+1		; Place MSB in input buffer
	LDA #writelomem		; JobCode for WriteLomem
	JSR OS_DECODEJOB	; Call the JobCall handler

which would read HIMEM, and then write LOMEM to be the same value, leaving no spare memory left.

The reason why you should not call an internal call from another internal call is that the same buffers (jobin_buf and jobout_buf) would be used for both calls, causing conflict between the two calls, or rather to the originator of the primary call ! The only time it would be safe if is if you have already got all the data you want from jobin_buf and haven't placed any in the jobout_buf (it would be lost if you had done).

OS_CALLOS[edit]

This OS call does various calls which are completely linked to the OS, everything from relocating code to reading and writing the hardware ports.

As normal, a "function" request value is placed in the A register, any extra data is put in the X and Y registers and a call to OS_CALLOS is made, and data is passed back in X, Y, c and z. The A register will return 0 for all routines except for calls 0 (deemed always to exist anyway), 12 (A is reserved for future use), 14 (returns 1 instead) and 15 (12, 14 and 15 do not return 12, 14, 15 in A though), if the routine passes back the same value in A (the exception being call 0) as you gave it then that OS_CALLOS function is not supported.

OS_CALLOS 0: Return the OS version number[edit]

Entry
  • A = 0
  • X, Y, c, z = undefined
Exit
  • A = Hardware release version
  • XY = OS version number
  • c, z is preserved
Note
  • Hardware version 0 is obsolete
  • Hardware version 1 is SmartBox
    • VIA at &8030
    • UART at &8010
    • ADC at &8000
    • AUX_PORT (inputs) at &8020

OS_CALLOS 1: Claim a block of JobCodes[edit]

Entry
  • A = 1
  • X = number of JobCodes required
  • Y, c, z = undefined
Exit
  • A = 0
  • X = base JobCode, if 0, no big enough block available
  • Y = Job ID number, 0 if no big enough block
  • c = set if no big enough block
  • z is preserved
Note

This call claims JobCodes codes from the JobCode list. JobCodes are in the range 0 to 255, 0 is special and is the JobCall "Blank". All the OS ones are spaced out but an "application" can claim a block of JobCodes, usually just the 1 call. If there is not a contigous block big enough then no JobCodes are allocated and the appropiate result is returned. The Job ID is from 0 to 255, 0 is unallocated, 1 is the OS calls, the rest are external applications, each successful call increments the Job ID returned, meaning a maximum of 255 successful calls (not important as their is not that many free calls available !), the Job ID can be used to check whether a JobCall request is yours.

OS_CALLOS 2: Handle NameCode request for the application[edit]

Entry
  • A = 2
  • XY = block pointing to block of JobNames
  • c, z = undefined
  • jobin_buf contains JobName (terminated by CR)
  • zero_gp3 = JobId (as returned by CALLOS 1)
Exit
  • A = 0
  • XY = undefined
  • z is preserved
  • if c = 1 (Name not found)
  • if c = 0 (Name found)
  • jobin_buf contains the JobCode
Note

Used via the internal_vec to convert from JobName to job code, the table format is just a list of the JobNames (each one CR terminated) terminated by a &FF

OS_CALLOS 3: Handle CodeName request for the application[edit]

Entry
  • A = 3
  • XY = block pointing to block of JobNames
  • c, z = undefined
  • jobin_buf contains JobCode
  • zero_gp3 = job id (as returned by CALLOS 1)
Exit
  • A = 0
  • XY = undefined
  • z is preserved
  • if c = 1 (JobCode not found)
  • if c = 0 (JobCode found)
    jobin contains JobName (terminated by CR)
Note

See above

OS_CALLOS 4: Relocate code[edit]

Entry
  • A = 4
  • XY = execution address (offset from start of code)
  • c, z = undefined
  • zero_gp1 = offset address of execution after relocation
  • zero_gp2 = offset address of relocation BitMap
  • zero_gp3 = length of code to relocate
Exit

No exit, calls begining of code straight away

Note

See later section on Relocation

OS_CALLOS 5: Describe the enviroment[edit]

Entry
  • A = 5
  • X, Y, c, z = undefined
Exit
  • A = 0
  • c, z is preserved
  • XY = address of table, format as such:
  • 1st byte: number of bytes (ie. 14 currently)
  • byte 2+:
    • address of VIA
    • address of ACIA
    • address of ADC
    • address of AUX_PORT
    • address of jobs_status
    • address of jobin_buf
    • address of jobout_buf
Note

It is preferred that this call is consulted first if direct use of the hardware is going to be used to get the correct addresses, or use OS_CALLOS 0 and decide what addresses/configuration to use from the Hardware version number.

OS_CALLOS 6: Read a register in the VIA[edit]

Entry
  • A = 6
  • X = register (0 to 15)
  • Y, c, z = undefined
Exit
  • A = 0
  • Y = value read
  • X, c, z is preserved
Note

The register number is not checked for range. This call allows access to the hardware without actually directly accessing the hardware yourself.

OS_CALLOS 7: Write to a register in the VIA[edit]

Entry
  • A = 7
  • X = register (0 to 15)
  • Y = value to write
  • c, z = undefined
Exit
  • A = 0
  • X, Y, c, z is preserved
Note

See above

OS_CALLOS 8: Read a register in the ACIA[edit]

Entry
  • A = 8
  • X = register (0 to 15)
  • Y, c, z = undefined
Exit
  • A = 0
  • Y = value read
  • X, c, z is preserved
Note

See above

OS_CALLOS 9: Write to a register in the ACIA[edit]

Entry
  • A = 9
  • X = register (0 to 15)
  • Y = value to write
  • c, z = undefined
Exit
  • A = 0
  • X, Y, c, z is preserved
Note

See above

OS_CALLOS 10: Read a register in the ADC[edit]

Entry
  • A = 10
  • X = register (0 to 15)
  • Y, c, z = undefined
Exit
  • A = 0
  • Y = value read
  • X, c, z is preserved
Note

See above

OS_CALLOS 11: Write to a register in the ADC[edit]

Entry
  • A = 11
  • X = register (0 to 15)
  • Y = value to write
  • c, z = undefined
Exit
  • A = 0
  • X, Y, c, z is preserved
Note

See above

OS_CALLOS 12: Read an ADC channel[edit]

Entry
  • A = 12
  • X = channel number to read (0 to 3)
  • Y, c, z = undefined
Exit
  • A = reserved for future use
  • if c = 0 (8 bit reading)
    • Y = reading
    • X = undefined
  • if c = 1 (16 bit reading)
    • X = reading (LSB)
    • Y = reading (MSB)
  • z = undefined
Note

The A register MAY not return 0, it is guaranteed though that it will not return 12.

OS_CALLOS 13: Read a register in the AUX_PORT[edit]

Entry
  • A = 13
  • X = register (0 to 15)
  • Y, c, z = undefined
Exit
  • A = 0
  • Y = value read
  • X, c, z is preserved
Note

See above

OS_CALLOS 14: Start sensor type checking (OS 2.066+)[edit]

Entry
  • A = 14
  • X, Y, c, z = undefined
Exit
  • A = 1
  • Y = value read
  • X, c, z is preserved
Note

A returns 1. This flags the ADC routines to start checking the sensors. After calling this routine, you should poll OS_CALLOS 15 to check when all the sensors have been checked.

OS_CALLOS 15: Check the status of sensor checking (OS 2.066+)[edit]

Entry
  • A = 15
  • X, Y, c, z = undefined
Exit
  • A = status
  • XY = pointer to sensor types block
  • c, z is preserved
Note

A does not return 0, though it is guaranteed to not return 15.

  • Status of 0 = checking finished and no futher check pending
  • Status of 1 = Waiting for current ADC channel to finish converting before switching sensor checking on
  • Status of >127 = Checking sensor types

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

OS_CALLOS 16: Set the OS' irq mask for the VIA (OS 2.069+)[edit]

Entry
  • A = 16
  • X = EOR mask
  • Y = AND mask
  • c, z = undefined
Exit
  • A = 0
  • X = old mask value
  • Y = new mask value
  • c, z is preserved

OS_CALLOS 17: Set the OS' irq mask for the ACIA (OS 2.069+)[edit]

Entry
  • A = 17
  • X = EOR mask
  • Y = AND mask
  • c, z = undefined
Exit
  • A = 0
  • X = old mask value
  • Y = new mask value
  • c, z is preserved

OS_CALLOS 18: Set the OS' irq mask for the ADC (OS 2.069+)[edit]

Entry
  • A = 18
  • X = EOR mask
  • Y = AND mask
  • c, z = undefined
Exit
  • A = 0
  • X = old mask value
  • Y = new mask value
  • c, z is preserved

OS_CALLOS 19: Set the ACIA's ctrl register and OS' soft copy (OS 2.069+)[edit]

Entry
  • A = 19
  • X = EOR mask
  • Y = AND mask
  • c, z = undefined
Exit
  • A = 0
  • X = old mask value
  • Y = new mask value
  • c, z is preserved

OS_CALLOS 20: Set the reset vector for battery back RAM support (OS 2.069+)[edit]

Entry
  • A = 20
  • XY = address to set reset vector or 0 for read only
  • c, z = undefined
Exit
  • A = 0
  • XY = old reset vector address
  • c, z is preserved
Note

Sets the reset vector to address supplied and sets check bytes in workspace.

OS_PRINTER: Place a character in the printer buffer[edit]

Entry
  • A = character to send
  • X, Y, c, z = undefined
Exit
  • A, X, Y is preserved
  • if c = 0 (printed)
    The printer is on and the character was inserted
  • if c = 1 (not printed)
    The printer is off and the character was forgotten
  • z = undefined
Note

The character goes into the printer buffer and will be actually sent to the printer when the printer asks for it. If the printer was not awake to begin with, an attempt is made to wake it up, thus meaning that in some circumstances when the printer wasn't ready a character may be lost, this cannot be helped. If the printer is off (via the BCD switch) then the character will be forgotten as the Printer Buffer is inactive.

Vectors[edit]

Some parts of the system are controlled by vectors, vectors also exist to patch the normal operation of some parts of the system. Vectors are RAM pointers, which are called to point to the routine to use. Being in RAM this means that they can be altered by the user to allow the user to modify the operation of part, or all, of a system call. All the call routines (eg. OS_DECODEJOB, OS_READBYTE etc.) are called via a RAM vector. There are vectors which the user patches to pick up unknown jobcodes, and to service requests like NameCode/CodeName etc.

The vectors are placed at address &200 in memory, each are 2 bytes long.

brk_vec &200 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.
nmi_vec &202 Called whenever there is a NMI request (not really applicable, as the NMI line is not connected to anything).
irq_vec &204 Called whenever there is an interupt.
irq2_vec &206 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.
sendserial_vec &208 Called when OS_SENDBYTE is called.
readserial_vec &20A Called when OS_READBYTE is called.
sendjob_vec &20C Called when OS_SENDJOB is called.
readjob_vec &20E Called when OS_READJOB is called.
decode_job_vec &210 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 "from the serial port".
unknownjob_vec &212 Called whenever an unknown JobCode is encountered.
extjob_vec &214 Called whenever the ExtendedJob is called, the A register holds the extension value.
centisec_vec &216 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.
internal_vec &218 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.
callos_vec &21A Called when OS_CALLOS is called.
printer_vec &21C Called when OS_PRINTER is called.
reset_vec &21E 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 "language" application to start up. Use OS_CALLOS 20 to set this vector.

All vectors should be 'daisy chained', ie. any routine which uses any of them should store the current value in its own workspace and alter the vector and at the end of the routine which services the vector should jump to the original saved value, if this isn't done the system may 'fall' down.

unknown, extendjob and irq2 all point to (in the present OS) to a routine which just returns. brk in the current OS just resets the stack and jumps back to the OS' main idle loop.

Irqs are reenabled before brk_vec is called

irq_vec[edit]

This is called when the 65c02 receives an irq from a device. As the 65c02 signals brk and irq through the same cpu vector the OS first finds out which one it really was and calls either brk_vec or irq_vec. It also stores the A reg at address &A0, as the A reg is corrupted while it finds out which vector to really call, any irq routine which has claimed the irq should restore the A reg from &A0 before returning via a RTI, else you should pass on the call to the old vector owner.

irq2_vec is called by the OS irq routine if it does not find a irq it can service (or as been masked out) in as-good-as the same conditions as irq_vec is called. The OS has a default irq2_vec handler of restoring A from &A0 and doing a RTI.

internal_vec and unknownjob_vec[edit]

The main operation of extended JobCalls rely on patching some vectors, two in fact. These two are unknownjob_vec and internal_vec. unknownjob_vec is the one which the OS calls to actually tell everybody that a JobCall has been requested which it does not know about (the JobCall MUST have been claimed before hand with OS_CALLOS, function 1, or else it is not passed on). internal_vec is the one which the OS calls in request to a NameCode or CodeName.

The vectors are defined as thus:

internal_vec[edit]

internal_vec 0[edit]
Purpose

Nothing

Entry
  • A = 0
  • X, Y, c, z = undefined
Exit

Leave everything as it is and exit straight away

Note

Used for claimed calls

internal_vec 1[edit]
Purpose

NameCode request

Entry
  • A = 1
  • X, Y, c, z = undefined
  • jobin_buf contains JobName (CR terminated)
Exit
  • if JobName recognised then
    • A = 0
    • first byte of jobin_buf contains matched JobCode
  • if JobName not recognised then
    • A = 1
    • X, Y, c, z = undefined
internal_vec 2[edit]
Purpose

CodeName request

Entry
  • A = 2
  • X, Y, c, z = undefined
  • jobin_buf contains JobCode
Exit
  • if JobCode recognised then
    • A = 0
    • jobin_buf contains matched JobName (CR terminated)
  • if JobCode not recognised then
    • A = 2
    • X, Y, c, z = undefined

All other calls (ie. not 0, 1 or 2 should be passed on straight away for future expansion)

unknownjob_vec[edit]

Purpose

To pass on unknown JobCalls to the correct owner

Entry
  • A = environment (0 = internal call, 1 = serial call)
  • Y = JobCode
  • X = JOB ID of owner
  • c, z = undefined
Exit
  • if jobcode yours then
    • if wrong environment then
      • A = 1
      • Y = 0
      • X, c, z = undefined
    • if right environment then
      • perform function
      • A = 0
      • Y = 0
      • X, c, z = undefined
  • if jobcode not yours then
    • pass on with registers unaltered

Starting Up[edit]

The setup procedure for setting up extension job calls is to:

  1. Claim the number of JobCodes you want
    if success:
  2. patch the internal_vec and unknownjob_vec vectors
  3. move LOMEM up to protect your program
  4. do anything else for initial startup you may want to do
  5. return back, ready and waiting

This in code looks like:

execute
	LDX #no_of_calls	; Number of JobCodes wanted
	LDA #1			; CALLOS, function 1, Claim JobCodes
	JSR OS_CALLOS		; Call CALLOS
	CPX #0			; Check we were given some codes
	BEQ noinstall		; No, don't bother installing ourself
	STX call		; Store base address of JobCodes allocated
	STY job_id		; Store JOB ID given

	LDA internal_vec	; Get old internal_vec (LSB)
	STA ovec		; Store it
	LDA internal_vec+1	; Get old internal_vec (MSB)
	STA ovec+1		; Store it
	LDA #>internal_handle	; Our internal handler (LSB)
	STA internal_vec 	; Place it in the vector (LSB)
	LDA #<internal_handle	; Our internal handler (MSB)
	STA internal_vec+1	; Place it in the vector (MSB)

	LDA unknownjob_vec	; Get old unknownjob_vec (LSB)
	STA ovec2 		; Store it
	LDA unknownjob_vec+1	; Get old unknownjob_vec (MSB)
	STA ovec2+1		; Store it
	LDA #>job_handle	; Our unknown job handler (LSB)
	STA unknownjob_vec	; Place it in the vector (LSB)
	LDA #<job_handle	; Our unknown job handler (MSB)
	STA unknownjob_vec+1	; Place it in the vector (MSB)

	LDX #>WriteLomem	; Point to JobName
	LDY #<WriteLomem
	JSR findjob		; Find WriteLomem code
	BEQ noinstall		; Doesn't exist !
	LDX #>end_of_code	; End of code (LSB)
	LDY #<end_of_code	; End of code (MSB)
	STX jobin_buf		; Place it in jobin_buf (LSB)
	STY jobin_buf+1		; Place it in jobin_buf+1 (MSB)
	JSR OS_DECODEJOB	; Call JobCall handler

noinstall
	RTS			; End - return to OS

findjob
	STX zero.gp1		; Zero page
	STY zero.gp1+
	LDY #0
findthisjoblo
	LDA (zero.gp1),Y
	STA jobin.buf,Y		; Store name in jobin_buf
	INY
	CMP #13
	BNE findthisjoblo
	LDA #3			; NameCode
	JSR OS.DECODEJOB	; Call NameCode
	LDA jobout.buf		; Get returned value
	RTS

WriteLomem	STR "WriteLomem"

This will setup the various vectors and memory pointers for the applicaion, the actual routines (internal_handle and job_handle) are fairly straight forward and simple:

internal_handle
	CMP #1			: Is it function call 1 (NameCode) ?
	BNE internal_handle_2	; No, check for function 2
	LDA job_id		; Get our JOB ID number
	STA zero_gp3		; Place it in zero_gp3 for CALLOS
	LDX #>job_names		; Our Job name table (LSB)
	LDY #<job_names		; Our Job name table (MSB)
	LDA #2			; CALLOS function 2
	JSR OS_CALLOS		; Call CALLOS
	BCC internal_yes	: Found, go off and claim call
	LDA #1			; Not found, restore A
	JMP (ovec)		; Call back to old vector

internal_handle_2
	CMP #2			; Is it function call 2 (CodeName) ?
	BNE internal_handle_3	; No, unknown, pass on
	LDA job_id		; Get our JOB ID
	STA zero_gp3		; Place it in zero_gp3 for CALLOS
	LDX #>job_names		; Our Job name table (LSB)
	LDY #<job_names		; Our Job name table (MSB)
	LDA #3			; CALLOS function 3
	JSR OS_CALLOS		; Call CALLOS
	BCC internal_yes	; Found, go off and claim call
	LDA #2			; Not found, restore A
	JMP (ovec)		; Call back to old vector

internal_yes
	LDA #0			; We want to claim call for some reason

internal_handle_3
	JMP (ovec)		; Call back to old vector

job_handle
	PHA			; Mark sure to preserve A
	CPY call		; Check call wanted against base of ours
	BCC job_handle_no	; Less than our base, so definately not ours
	TYA
	SBC #no_of_calls	; Subtract number of calls we have
	CMP call		; Now compare with base
	BCC job_handle2		; Yes, one of ours

job_handle.no
	PLA			; Restore A
	JMP (ovec2)		; Call back to old vector

job_handle2
	TYA
	SEC
	SBC call		; Subtract our base from call
	ASL A			; Times by 2 for offset into table
	TAY			; Place in Y
	LDA job_run_table,Y	; Get LSB of routine
	STA zero_gp1		; Store it for indirect jump (LSB)
	LDA job_run_table+1,Y	; Get MSB of routine
	STA zero_gp1+1		; Store it for indirect jump (MSB)
	PLA			; Restore A
	JMP (zero_gp1)		; Call our relevant routine

job_names			; List of our JobNames
	STR "TestJob"		; Test job name
	DFB &FF			; &FF - marks end of table

job_run_table			; List of routine addresses to match Jobs
	DFW job_TestJob		; Test job routine

Relocation[edit]

The relocation method used will not do a complete relocation, ie. from ANY address to ANY address. The only constraint on the addresses is that they must be the same offset in the page, which means, &2602 and &3402 is alright but &2602 and &3455 is not, ie. the difference between the two addresses must be a multiple of 256 bytes, as only the MSB is altered, this should not cause any real problems. What is meant by the two addresses is the address the code was originally assembled at and the address it wants relocating to. Because of this programs must be multiples of 256 bytes, ie. the low byte of LOMEM must ALWAYS be 0, your program should be padded out.

The relocation mechanism works by having a table, and to each byte of code there is one BIT, if unset it means the byte of code should not be relocated, else it should be, thus the BitMap table is 8 times smaller than the code. To generate the BitMap you have to assemble the code at two different places, and compare both sets of assembled code against each other, the differences are where the code is to be relocated. The code which is sent up to the controller is assumed to be assembled at address &100, so the first code should be assembled at &100, the other one should be assembled at another address, say &600, and the first one be sent up with the BitMap. A program is availble for the Elk/Beeb/Arc with 65Tube/Mac with BBC BASIC to compare the two files and create the BitMap.

All the program has to do to relocate itself is execute this bit of code first of all: (address is the base address of the program, ie. &100 for the downloaded version)

execcall
	LDA #>(execute-address)	; Final execution address offset (LSB)
	STA zero_gp1
	LDA #<(execute-address)	; Final execution address offset (MSB)
	STA zero_gp1+1

	LDA #>(BitMap-address)	; BitMap address offset (LSB)
	STA zero_gp2
	LDA #<(BitMap-address)	; BitMap address offset (MSB)
	STA zero_gp2+1

	LDA #>(execcall-address); Length of data to relocate (LSB)
	STA zero_gp3
	LDA #<(execcall-address); Length of data to relocate (MSB)
	STA zero_gp3+1

	TXA			; Execcall REAL address
	SEC
	SBC #>(execcall-address); Subrtract length
	TAX			; Put it back in X
	TYA			; Ditto for MSB
	SBC #<(execcall-address)
	TAY
	LDA #4			; CALLOS function for Relocate
	JMP OS_CALLOS		; Call CALLOS

BitMap				; BitMap start

This will relocate the code and automatically call the execution address, which should be the initialisation code (ie. setup internal_vec and unknownjob_vec etc.). "address" is the address the code is started to assemble at, &100 for the version to be uploaded. The upload routine should pass in X and Y (via ExecuteCode) the address of the start of execcall, this means that the routine can tell where it is in memory, subtracting the length gives the start of the code to relocate.

Layout Summary[edit]

The layout of a full application should be like this by now:

	start of code
		program job codes, data etc.
		internal handler
		job handler
	end of code
		initialisation (setup internal etc.)
	end of code to be relocated
		execution (relocation)
		relocation BitMap

Because of memory space the initialisation code should be placed outside the reserved memory limit of the program, as it is only called once and not needed again.

Modules[edit]

The module is located at &8000 in memory, and should be a EPROM placed in the second ROM socket in the controller (ie. the top RAM socket). This module is primary designed to turn the Controller into a dedicated task, making it perform some function from switch on. It could also be used to add more permant JobCalls to the Controller, in the process taking up much less RAM), but because of there being only one Module present this feature is limited, and is much more suited to a collection of the user's own personal routines.

For the OS to recognise a module as being in place the text "Module" is looked for, the layout of a module should be like this (starting at &8000):

Offset Value Comment
0 "Module" The OS checks for this string
6 0 End of check string
7 Language entry Address of 'Language' entry
9 Service entry Address of 'Service' entry
11 "<Module Title>" Module title
0 End of Module title
"<Module part>" Part Module Title
0 End of Part Module Title
"x.xx" Part version number
&FF End of parts list

The language entry is a 2 byte value holding the address of the language entry routine, this is called on reset, if the BCD is set correctly. A value of less than &8000 means there is no valid language entry and any language calls are ignored. If a RTS is made the OS carries on as it would have if there wasn't a language call.

The service entry is a 2 byte value holding the address of the service entry routine, this is called at various appropriate moments with service numbers in A. A list of current calls is thus:

  • 0 - Not used
  • 1 - Unknown Job Code
  • 2 - Centisecond call
  • 3 - irq2 (unknown IRQ)
  • 4 - internal_vec call (exit with A=0 to claim)
  • 254 - RESET
  • 255 - BRK

On exit all registers and flags should be preserved. For call 1 the unknown job value is held in the Y register, the environment is held in the X register, on exit, X should contain the flag, as A does from the exit of unknownjob_vec. For the rest of calls, X and Y are undefined.

For both Service and Language entry points, a value of 0 means "there is no valid routine" and the module isn't called for that type of entry.

Example JobCall[edit]

Finally a full implementation of a JobCall, from start to finish.

DIM data% &1000
:
no_of_calls = 1
:
VIA = &E030
ACIA = &E010
ADC = &E000
AUX_PORT = &E020
brk_vec = &200
nmi_vec = &202
irq_vec = &204
irq2_vec = &206
sendserial_vec = &208
readserial_vec = &20A
sendjob_vec = &20C
readjob_vec = &20E
decodejob_vec = &210
unknownjob_vec = &212
extjob_vec = &214
centisec_vec = &216
internal_vec = &218
callos_vec = &21A
printer_vec = &21C
zero_gp1 = 0
zero_gp2 = 2
zero_gp3 = 4
zero_gp4 = 6
zero_gp5 = 8
zero_gp6 = 10
zero_gp7 = 12
zero_gp8 = 14
zero_gp9 = 16
zero_gp10 = 18
user_reserved = &70
irq_A = &A0
fcount = &A1
RAM_size = &A3
jobout_buf = &400
jobin_buf = &480
OS_PRINTER = &FFB9
OS_CALLOS = &FFBC
OS_SENDBYTE = &FFBF
OS_READBYTE = &FFC2
OS_SENDJOB = &FFC5
OS_READJOB = &FFC8
OS_DECODEJOB = &FFCB
:
FOR create=1 TO 2
  :
  FOR pass%=4 TO 7 STEP 3
    P%=&100*create : O%=data%
    [OPT pass%
    :
    .job_DemoJob
    CMP #1
    BEQ DemoJob_go
    LDA #1
    LDY #0
    RTS
    /
    .DemoJob_go




    :
    .internal_handle
    CMP #1; Is it function call 1 (NameCode) ?
    BNE internal_handle_2; No, check for function 2
    /
    LDA job_id; Get our JOB ID number
    STA zero_gp3; Place it in zero_gp3 for CALLOS
    LDX #job_names MOD 256; Our Job name table (LSB)
    LDY #job_names DIV 256; Our Job name table (MSB)
    LDA #2; CALLOS function 2
    JSR OS_CALLOS; Call CALLOS
    BCC internal_yes; Found, go off and claim call
    LDA #1; Not found, restore A
    JMP (ovec); Call back to old vector
    /
    .internal_handle_2
    CMP #2; Is it function call 2 (CodeName) ?
    BNE internal_handle_3; No, unknown, pass on
    /
    LDA job_id; Get our JOB ID
    STA zero_gp3; Place it in zero_gp3 for CALLOS
    LDX #job_names MOD 256; Our Job name table (LSB)
    LDY #job_names DIV 256; Our Job name table (MSB)
    LDA #3; CALLOS function 3
    JSR OS_CALLOS; Call CALLOS
    BCC internal_yes; Found, go off and claim call
    LDA #2; Not found, restore A
    JMP (ovec); Call back to old vector
    /
    .internal_yes
    LDA #0; We want to claim call for some reason
    /
    .internal_handle_3
    JMP (ovec); Call back to old vector
    :
    .job_handle
    PHA; Make sure to preserve A
    CPY call; Check call wanted against base of ours
    BCC job_handle_no; Less than our base, so definately not ours
    TYA
    SBC #no_of_calls; Subtract number of calls we have
    CMP call; Now compare with base
    BCC job_handle2; Yes, one of ours
    /
    .job_handle.no
    PLA; Restore A
    JMP (ovec2); Call back to old vector
    /
    .job_handle2
    TYA
    SEC
    SBC call; Subtract our base from call
    ASL A; Times by 2 for offset into table
    TAY; Place in Y
    LDA job_run_table,Y; Get LSB of routine
    STA zero_gp1; Store it for indirect jump (LSB)
    LDA job_run_table+1,Y; Get MSB of routine
    STA zero_gp1+1; Store it for indirect jump (MSB)
    PLA; Restore A
    JMP (zero_gp1); Call our relevant routine
    :
    .job_names; List of our JobNames
    EQUS "DemoJob":EQUB 13; Test job name
    /
    EQUB &FF; &FF - marks end of table
    :
    .job_run_table; List of routine addresses to match Jobs
    EQUW job_DemoJob; Test job routine
    :
    .call   : EQUB 0; Store for our JobCode base
    .job_id : EQUB 0; Store for our JobId
    .ovec   : EQUW 0; Store for old internal_vec value
    .ovec2  : EQUW 0; Store for old unknownjob_vec value
    :
    EQUS STRING$(&100-(P% MOD 256),CHR$0)
    .end_of_code; end_of_code should be on page boundry
    :
    .execute
    LDX #no_of_calls; Number of JobCodes wanted
    LDA #1; CALLOS, function 1, Claim JobCodes
    JSR OS_CALLOS; Call CALLOS
    STX call; Store base address of JobCodes allocated
    STY job_id; Store JOB ID given
    CPX #0; Check we were given some codes
    BEQ noinstall; No, don't bother installing ourself
    /
    LDA internal_vec; Get old internal_vec (LSB)
    STA ovec; Store it
    LDA internal_vec+1; Get old internal_vec (MSB)
    STA ovec+1; Store it
    LDA #internal_handle MOD 256; Our internal handler (LSB)
    STA internal_vec; Place it in the vector (LSB)
    LDA #internal_handle DIV 256; Our internal handler (MSB)
    STA internal_vec+1; Place it in the vector (MSB)
    /
    LDA unknownjob_vec; Get old unknownjob_vec (LSB)
    STA ovec2; Store it
    LDA unknownjob_vec+1; Get old unknownjob_vec (MSB)
    STA ovec2+1; Store it
    LDA #job_handle MOD 256; Our unknown job handler (LSB)
    STA unknownjob_vec; Place it in the vector (LSB)
    LDA #job_handle DIV 256; Our unknown job handler (MSB)
    STA unknownjob_vec+1; Place it in the vector (MSB)
    /
    LDX #end_of_code MOD 256; End of code (LSB)
    LDY #end_of_code DIV 256; End of code (MSB)
    STX jobin_buf; Place it in jobin_buf (LSB)
    STY jobin_buf+1; Place it in jobin_buf+1 (MSB)
    LDA #65; JobCode for WriteLomem
    JSR OS_DECODEJOB; Call JobCall handler
    /
    .noinstall
    RTS; End - return to OS
    :
    .execcall
    LDA #>(execute-address); Final execution address offset (LSB)
    STA zero_gp1
    LDA #<(execute-address); Final execution address offset (MSB)
    STA zero_gp1+1
    /
    LDA #>(BitMap-address); BitMap address offset (LSB)
    STA zero_gp2
    LDA #<(BitMap-address); BitMap address offset (MSB)
    STA zero_gp2+1
    /
    LDA #>(execcall-address); Length of data to relocate (LSB)
    STA zero_gp3
    LDA #<(execcall-address); Length of data to relocate (MSB)
    STA zero_gp3+1
    /
    TXA; Execcall REAL address
    SEC
    SBC #>(execcall-address); Subtract length
    TAX; Put it back in X
    TYA; Ditto for MSB
    SBC #<(execcall-address)
    TAY
    LDA #4; CALLOS function for Relocate
    JMP OS_CALLOS; Call CALLOS
    /
    .BitMap; BitMap start
    :
    ]
  NEXT
  :
  OSCLI "SAVE code"+STR$create+" "+STR$~data%+" "+STR$~O%
  :
NEXT

Changelog[edit]

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 &440
jim 072.d1 Moved rs.inp.buf to &500
jim 072.d1 Moved jobout.buf to &600
jim 072.d1 Moved jobin.buf to &680
jim 072.d1 Moved jobs.status to &700
jim 072.d1 Added irq.X (&A4)
jim 072.d1 Added irq.Y (&A5)
jim 072.d1 Removed short detection code
jim 072.d1 Voided CallOS' 6,7,8,9,10,11,13,16,17,18,19
jim 072.d1 Removed JobCalls ReadADCReg,WriteADCReg
jim 072.d1 Removed JobCalls ReadACIAReg,WriteACIAReg
jim 072.d1 Removed JobCalls ReadVIAReg,WriteVIAReg,SetVIAHigh,SetVIALow
jim 072.d1 Added CallOS 21 Write Outputs
jim 072.d1 Added CallOS 22 Read Outputs added
jim 072.d1 Added CallOS 23 Read Inputs added
jim 072.d1 Added CallOS 24 Write Motors
jim 072.d1 Added CallOS 25 Read Motors
jim 072.d1 Added CallOS 26 Read Keypad
jim 072.d1 Expanded CallOS 5 information
jim 072.d1 Added JobCall IdentSystem to return CallOS 5 info
jim 072.d1 JobCalls now use appropriate CallOS'
jim 072.d1 OS.READBYTE no longer preserves A on CS
jim 072.d1 irq.vec,irq2.vec,nmi.vec now redundant
jim 072.d1 Renamed JobCall DownloadData to DownloadData38
jim 072.d1 Renamed JobCall UploadData to UploadData38
jim 072.d1 Renamed JobCall ExecuteCode to ExecuteCode38
jim 072.d1 Renamed JobCall ReadByte to ReadByte38
jim 072.d1 Renamed JobCall StoreByte to StoreByte38
jim 072.d1 JobCall ForcedADCRead now checks adc.owner first
jim 072.d3 Added CallOS 27 Write printer
jim 072.d3 Added CallOS 28 Read printer
jim 072.d3 Added CallOS 29 Write RTC Reg
jim 072.d3 Added CallOS 30 Read RTC Reg
jim 072.d3 Added CallOS 31 Write RTC string
jim 072.d3 Added CallOS 32 Read RTC string
jim 072.d3 Added CallOS 33 Write RTC bcd
jim 072.d3 Added CallOS 34 Read RTC bcd
jim 072.d3 Added CallOS 35 Write LCD Reg
jim 072.d3 Added CallOS 36 Read LCD Reg
jim 072.d3 Added JobCall WritePrinter
jim 072.d3 Added JobCall ReadPrinter
jim 072.d3 Added JobCall PrintChar
jim 072.d3 Added JobCall PrintStreamZ
jim 072.d3 Added JobCall PrintStream
jim 072.d3 Added JobCall PrintServer
jim 072.d3 Added JobCall WriteRTCReg
jim 072.d3 Added JobCall ReadRTCReg
jim 072.d3 Added JobCall WriteRTC
jim 072.d3 Added JobCall ReadRTC
jim 072.d3 Added JobCall WriteRTCbcd
jim 072.d3 Added JobCall ReadRTCbcd
jim 072.d3 Added JobCall WriteLCDReg
jim 072.d3 Added JobCall ReadLCDReg
jim 072.d3 Added CallOS 37 Write Power ctrl
jim 072.d3 Added CallOS 38 Read Power ctrl
jim 072.d3 Implemented OS.PRINTER
jim 072.d3 Added JobCall PatchMF (MotorForward)
jim 072.d3 Changed irq.vec to int0.vec
jim 072.d3 Changed irq2.vec to int1.vec
jim 072.d3 Added int2irq.vec (wrksp+&20)
jim 072.d3 Added int3irq.vec (wrksp+&22)
jim 072.d3 Added int4irq.vec (wrksp+&24)
jim 072.d3 Added c0irq.vec (wrksp+&26)
jim 072.d3 Added c1irq.vec (wrksp+&28)
jim 072.d3 Added t1irq.vec (wrksp+&2A)
jim 072.d3 Added t2irq.vec (wrksp+&2C)
jim 072.d3 Added txirq.vec (wrksp+&2E)
jim 072.d3 Added tyirq.vec (wrksp+&30)
jim 072.d3 Added s1rirq.vec (wrksp+&32)
jim 072.d3 Added s1rirq.vec (wrksp+&34)
jim 072.d3 Added s2irq.vec (wrksp+&36)
jim 072.d3 Added adcirq.vec (wrksp+&38)
jim 072.d3 Renamed JobCall DownloadData38 to DownloadData740
jim 072.d3 Renamed JobCall UploadData38 to UploadData740
jim 072.d3 Renamed JobCall ExecuteCode38 to ExecuteCode740
jim 072.d3 Renamed JobCall ReadByte38 to ReadByte740
jim 072.d3 Renamed JobCall StoreByte38 to StoreByte740
jim 072.d4 Hardware update
jim 072.d4 Re-assigned CallOS 37 to Write Power/Charge control
jim 072.d4 Re-assigned CallOS 38 to Read Power/Charge control
jim 072.d4 Moved int2irq.vec to wrksp+&22
jim 072.d4 Moved int3irq.vec to wrksp+&24
jim 072.d4 Moved int4irq.vec to wrksp+&26
jim 072.d4 Moved c0irq.vec to wrksp+&28
jim 072.d4 Moved c1irq.vec to wrksp+&2A
jim 072.d4 Moved t1irq.vec to wrksp+&2C
jim 072.d4 Moved t2irq.vec to wrksp+&2E
jim 072.d4 Moved txirq.vec to wrksp+&30
jim 072.d4 Moved tyirq.vec to wrksp+&32
jim 072.d4 Moved s1rirq.vec to wrksp+&34
jim 072.d4 Moved s1rirq.vec to wrksp+&36
jim 072.d4 Moved s2irq.vec to wrksp+&38
jim 072.d4 Moved adcirq.vec to wrksp+&3A
jim 072.d4 Added OS.LCDVDU (&FFB7) and lcdvdu.vec (wrksp+&20)
jim 072.d5 Re-assigned CallOS 37 to Write/Read Power/Charge control
jim 072.d5 Re-assgined CallOS 38 Read Battery Voltage
jim 072.d5 OS.CALLOS now re-entrant
jim 072.d5 Fixed OS.PRINTER
jim 072.d5 Started to add internal logging software
jim 072.d5 Moved OS.LCDVDU to &FFB6
jim 072.d7 Added OS.PRINTERPOLL (&FFB3) and printerpoll.vec (wrksp+&3C)
bob 073 Matched jim & bob sensor lookup tables
bob 073 Added PatchMF
jim 072.dj Added OS.CALLOS 51 Read keypad press
jim 072.dj Added hard reset keypad press
jim 072.dk Added JobCall ReadOutputs
jim 072.do Changed soft reset of zero page locations
jim 072.do Changed MotorForward/Backward bitmaps
jim 072.dp Fixed OS.CALLOS WriteRTCbcd
jim 072.dq Fixed OS.CALLOS ReadRTCstring for correct 24hr operation
jim 072.dr Changed reset prompts
bob 074 Added support for Little Bob
jim 072.dy Added further RTC/CMOS support
jim 072.dB Removed double hard reset
jim 072.dE Set _cpu.mode depending on external memory requirement
jim 072.dF Added Insight code
jim 073 Sub-release
jim 074 Modification to LCD code - uses bsy flag all the time now
jim 074 Fixed OS.CALLOS 42 Write LCD Char Def
jim 074 Fixed OS.CALLOS 43 Read LCD Char Def
jim 075 Added simple battery charge code
jim 075 Fixed JobCall ReadSensorTable
jim 075 Fixed OS.CALLOS 42 Write LCD Char Def
jim 075 Fixed OS.CALLOS 43 Read LCD Char Def
jim 075 Added sleep code
jim 075 Added OS.CALLOS 52 Write Sleep Time
jim 075 Added OS.CALLOS 53 Read Sleep Time
jim 075 Modified battery charge code
jim 076 Modified JobCall ReadSensorTable
jim 076 Modified OS.CALLOS 26 ReadKeypad to do debounce
jim 077 Rewrote battery charging code
jim 077.2 Reduced stop charge threshold to 2
jim 077.3 Increased stop charge threshold to 3
jim 077.3 Added battery voltage averaging
bill 077 Stripped
bill 077 Moved ins to out port
bill 077 Added RTS
bill 077 Changed sensor ID system back to original, using p2.1
bill 077 Dedicated USB/RS selection line, RTS provisioned on p2.3
bill 077 Overload "bounce" timing
bill 078 CTS added
bill 079 Temperature fudge