I’ve looked into a lot of the internals of the Loxone Miniserver (the CAN bus protocols, the SD card file system, etc) and written it down with a lot of sample code at https://github.com/sarnau/Inside-The-Loxone-Miniserver. For the ambitious i’ve also provided an STM32 ARM implementation of the Loxone Link/Loxone Tree protocol for a lot of hardware at https://github.com/sarnau/LoxLink
Category: Reverse Engineering
Communication protocol of the Proteus EcoMeter TEK603
The receiver device has a USB port, which has a CP2102 USB<->RS232 converter. With 115200 baud, 8N1 it is possible to communicate with the device.
Package format
All data to or from the device is transmitted as a package.
- 2 byte header (‘SI’ = 0x53,0x49)
- 2 byte length of the complete package (16 bit, big-endian)
- 1 byte command (1: data send to the device, 2: data received from the device)
-
1 byte flags:
- bit 0: set the clock (hour/minutes/seconds) in the device on upload
- bit 1: force reset the device (set before an update of the device)
- bit 2: a non-empty payload is send to the device
- bit 3: force recalculate the device (set on upload after changing the Sensor Offset, Outlet Height or the lookup table)
- bit 4: live data received from the device
- bit 5: n/a
- bit 6: n/a
- bit 7: n/a
-
1 byte hour – used to transmit the current time to the device
- 1 byte minutes
-
1 byte seconds
-
2 byte eeprom start (16 bit, big-endian) – unused in live data
-
2 byte eeprom end (16 bit, big-endian)
-
n bytes payload
-
2 byte CRC16 (16 bit, big-endian)
The app never always read more than about 800 bytes of data at once, e.g. reading the history is done in 4 reads:
- 0x0000..0x0031 Header
- 0x0032..0x0289 Offsets
- 0x028a..0x03e5 This 365 entry history
- 0x03e6..0x06d0 ..continuation of the history
Live data
With an open connection and without significant change of the fluid level, the current level is send every 30/60 minutes (water/oil) to the computer. There seems to be no way to request it. Because the wireless transmitter only sends the data in that frequency, it probably makes very little sense.
If the level changes (refilling the tank) it updates much more often.
Live data has the following payload:
- 1 byte temperature in Fahrenheit + 40. The get the temperature in Celcius you need to calculate it like this =>
temperatureC = ((temperatureF - 40 - 32) / 1.8)
- 2 byte sensor level in cm (16-bit, big-endian)
- 2 byte usable capacity in l (16-bit, big-endian)
- 2 byte full capacity of the tank in l (16-bit, big-endian)
EEPROM content
The EEPROM has three distinct areas:
- Header and configuration
- tank air space table (liters of tank volume based on the level above the fluid (0..300cm))
- 365 days of average usage – a round-robin buffer
Header and configuration
If there is no description, the value is unknown.
- 0x00: 0xcc (204) Magic byte, always checked to test the communication with the device.
- 0x01: 0x18 0x3D 0xff 0xca
- 0x05: 0x03 Type of the tank A..D (0..3) The tank offset table is calculated by the device based on the setting (A-C). If the table is modified via a computer, the type is set to D (3).
- 0x06: 0x0096 Height of the tank in cm (here: 150)
tankHeight
- 0x08: 0x0048 Width of the tank? (Probably entered during configuration)
- 0x0a: 0x0064 Height of the tank without the rounding? (Probably entered during configuration)
- 0x0c: 0x2710 Volume of the tank (10000l)
- 0x0e: 0x01
-
0x0f: 0x00
-
0x10: 0x00
- 0x11: 0x00 0/1 Alarm Off/On
- 0x12: 0x01
- 0x13: 0x002d 45 Index to todays entry in the history
historyIndex
. Because the history starts yesterday, you need to start with the next index for yesterday. - 0x15: 0x02
- 0x16: 0x00
- 0x17: 0x43
- 0x18: 0x00
- 0x19: 0x34
- 0x1a: 0x00
- 0x1b: 0x3a
- 0x1c: 0x05db (16-bit, big-endian, cost of fuel: 0.00 – 15.00 currency/l)
- 0x1e: 0x00
-
0x1f: 0x00
-
0x20: 0x00
- 0x21: 0x00
- 0x22: 0x00
- 0x23: 0x00
- 0x24: 0x00
- 0x25: 0x00
- 0x26: 0x00
- 0x27: 0x00 Sensor Offset
sensorOffset
in cm (0..70cm) - 0x28: 0x08 Outlet Height
outletHeight
in cm (0..70cm) - 0x29: 0x1b
- 0x2a: 0x00
- 0x2b: 0x03
- 0x2c: 0x1c
- 0x2d: 0x00
- 0x2e: 0x94
- 0x2f: 0x00
- 0x30: 0x00 0xDD
Tank Air Space Table tankAirSpaceTable
The distance from the bottom of the transmitter to the top of the fluid. The table has one value per cm tankHeight
– up to 300cm is supported. If the distance between the top and the transmitter is 0, the capacity is the maximum value. This table is calculated by the tank type A-C automatically, but can be customized to match any shape (tank type D). It is used to convert the level in cm into liter.
The outletHeight
is an offset that is added to the level to return the volume of the tank. It can be used to define a reserve.
History
The history table is 365 entries large. Each entry is 3 bytes. It is a cyclic buffer with an index pointer in the header pointing at todays position. To go into the past increment the index and wrap around at 365 till you hit today again.
The 3 bytes have this format: 0xaa 0xbc 0xcc
- ‘aa’ changes up/down a bit, but it is also not clear what it means.
- ‘b’ also unknown. I’ve only seen values of 1 or 2 for it.
- ‘ccc’ is the average oil usage in 0,1l on that day (divide by 10 to get liters). 0 if there was no oil used on a day.
The level of the tank based on todays usage is estimated in liters as follows:
usage = ccc / 10 - sensorOffset
lowValue = tankAirTable[(int)usage]
highValue = tankAirTable[(int)usage+1]
usage = highValue + (lowValue - highValue) * (1 - (usage - (int)usage))
usage = usage - tankAirTable[tankHeight - outletHeight]
Loxone MicroSD Cards
Loxone states the following on their website:
Use only Loxone SD cards for the Miniserver. All SD cards have their own CPU which manages the flash memory. For optimum performance, Loxone OS accesses many low-level functions of the SD card, unlike, for example, a digital camera.
Wow, this is exciting! What mystery low level functions are they using? Time to find out.
The server ships with a 4GB Micro SD Card, which is of the SDHC type.
SD cards exists in many different varieties, but have to follow the specifications from the SD Group. This includes the communication on the bit level, as well as how commands are executed. However, there are options to have custom commands or at least non-mandatory commands. It is also possible to only support a subset of commands for a manufacturer, if they supply specific SD cards. Loxone could do that, but it is not probable, just because it makes it really hard for them to switch vendors for their SD cards, if prices and or availability changes. You don’t want to release an update to the Miniserver for different types of SD cards.
As I already looked at the mainboard of the Miniserver, it seems the CPU is talking directly to the SD card. That is not a surprise, considering that even Arduinos can do that easily. The Miniserver does that with the help of some gate logic, probably for buffering. I didn’t really care to look into the exact implementation details too much here.
Considering that the physical communication is standardized and that the actual firmware is read from the SD card by the boot code from the flash memory, I don’t expect too much special code for reading. And I am correct: the initialization phase for the SD card seems to follow the specification from the SD Group perfectly and can detect all types of SD cards up to SDHC.
The detailed information collected during initialization about the card can be requested from Loxone Config via “Detailed Device Info”:
SD-Test: SD performance read=403kB/s write=345kB/s no error (0 0), ManufactorerID 2 Date 2016/3 CardType 2 Blocksize 512 Erase 0 MaxtransferRate 25000000 RWfactor 2 ReadSpeed 22222222Hz WriteSpeed 22222222Hz MaxReadCurrentVDDmin 3 MaxReadCurrentVDDmax 5 MaxWriteCurrentVDDmin 5 MaxWriteCurrentVDDmax 1 Usage:1.71%
What does these things mean?
- read = measured data read performance in kB/s after 1MB of reading a test file
- write = measured data read performance in kB/s after 1MB of writing a test file
- no error (0 0) = number of errors with this SD card (first number/error code = 0: no error, 1: read error, 2: verify error, second number: number of errors)
- ManufactorerID (yes, that is probably an original Austrian typo) = The manufacturer of the card. The ID is assigned by the SD Group.
- Date = Manufacturing date of the card
- CardType = 0 = unknown, 1 = SDv2, 2 = SDHC, 3 = SDv1 – detected during initialization
- Blocksize = 512 bytes (I think it this is true for all cards)
- Erase = DATA_STAT_AFTER_ERASE from the SCR register. Defines the data status after erase.
- MaxtransferRate = TRAN_SPEED from the CSD register. Maximum data transfer rate per one data line in bit/s
- RWfactor = R2W_FACTOR from CSD register (0 = 1, 1 = 2 (write half as fast as read) , 2 = 4, 3 = 8, 4 = 16, 5 = 32)
- ReadSpeed = 133333333 / (2 * ((133333333 / (2 * MaxtransferRate)) + 1))
- WriteSpeed = 133333333 / (2 * ((133333333 / (2 * MaxtransferRate)) + 1)) (always identical to ReadSpeed)
- MaxReadCurrentVDDmin = VDD_R_CURR_MIN from CSD register (0=0.5mA; 1=1mA; 2=5mA; 3=10mA; 4=25mA; 5=35mA; 6=60mA; 7=100mA)
- MaxReadCurrentVDDmax = VDD_R_CURR_MAX from CSD register (0=1mA; 1=5mA; 2=10mA; 3=25mA; 4=35mA; 5=45mA; 6=80mA; 7=200mA)
- MaxWriteCurrentVDDmin = VDD_W_CURR_MIN from CSD register (0=0.5mA; 1=1mA; 2=5mA; 3=10mA; 4=25mA; 5=35mA; 6=60mA; 7=100mA)
- MaxWriteCurrentVDDmax = VDD_W_CURR_MAX from CSD register (0=1mA; 1=5mA; 2=10mA; 3=25mA; 4=35mA; 5=45mA; 6=80mA; 7=200mA)
- Usage = How much of the SD card is used by data
The maximum supported capacity is 16GB, so do not use larger ones.
All the technical information is reported from the SD card and only for information. After initialization, what else does the Miniserver do to communicate to the card?
- Reading blocks via Block Read (CMD17)
- Writing blocks via Block Write (CMD18)
- Erasing blocks by simply writing empty blocks
- Repair after errors: power cycle the SD card and reinitialize the card (just as when the server is booting)
There are three different types of SD Card errors possible:
- Hardware errors during reading/writing. They occur within the low-level communication with the SD card itself. In this case a repair is automatically tried.
- CRC error. The Loxone filesystem has checksums over each block. If they don’t match, it is an error – this could potentially happen, if the SD card is defect. But the software also tries to do a repair.
- SD card is full. The Miniserver tries to write data, but there is no more space on the SD card.
The Miniserver only uses mandatory commands, this means every single SD card in the market has to implement them. Therefore any SD card from any good manufacturer should work just fine in the Miniserver. That said: there are Chinese manufacturers who sell SD cards with a limited actual capacity (e.g. 2GB) but which identify themselves as e.g. 16GB cards. This will result in data loss!
Inside the Loxone Miniserver
I’d like to explain some technical details of the Loxone Miniserver. The Miniserver (as well as all extensions) are ARM based, just like all modern mobile phones. It is clocked at just 8MHz.
The Miniserver is ARM based. The CPU is booting from a 512kb flash memory. This code then loads the actual operating system from the SD Card into the additional 64MB of memory and executes it from there.
CPU, Flash Memory, SRAM
The CPU is an Atmel AT91SAM9G20 from Microchip. It is a 3,3V 400MHz ARM926 with 32kb internal SRAM and 64kb internal ROM. It is paired with a serial interface Flash memory (AT25DF041A from Adesto Technologies), which is updatable by Loxone – it is one of two chips mounted on the back of the board. This flash memory also contains non-volatile memory used by the Miniserver, like encryption keys, which are not stored on the SD card. The other important chips are two SD RAM chips (H57V2562GTR from SK Hynix) as additional memory with 256MBit each adding another 64MB of memory.
Ethernet
The Ethernet is connected with another Chip from Microchip, the KSZ8051RNL1. Which is a 10BASE-T/100BASE-TX Automotive Physical Layer Transceiver. It doesn’t offer a lot, so most of the load for the different protocols TCP/IP and UDP, ARP, DHCP, etc. are all handled by the CPU.
SD Card
The SD Card is accessed with some logic gate directly. Nothing special here.
RTC Clock
The Miniserver has a battery backed CMOS Real-Time Clock (RTC) via a PCF2123 from NXP Semiconductors. This allows the system to run without an internet connection, while still having a valid time. During boot it is set if possible by testing various NTP servers.
Relays / Digital Out
The Relays are HF33024-HLT, which are isolated from the CPU by two ADuM3401 from Analog Devices.
Analog Out
The Analog Out are driven by a AD5724 also from Analog Devices, which is a a complete, quad, 12-/14-/16-Bit, serial input, unipolar/bipolar voltage output DAC. They are driven to have a 10V output range with a 12-bit resolution.
Analog In
The Analog Ins are driving by a TV1544 from Texas Instruments. It is a CMOS 10-bit switched-capacitor successive-approximation (SAR) analog-to-digital (A/D) converter.
Digital In
The digital inputs are read by a HVS882 from Texas Instruments. It is an 8-channel digital input serializer, which can handle up to 34V at the inputs with a flexible current limiter – it therefore also protects the CPU from damage. This is the other chip, which is mounted on the back of the board.
Loxone Link
The Loxone Link bus is standard CAN bus connected via a CAN controller (MCP2515, also Microchip) to the CPU. It is using a standard CAN Transceiver (SN65HVD232D from Texas Instruments) to protected the Miniserver from defects on the bus. The Miniserver also has a 120Ω resistor built-in, so it has to be on the end of the CAN bus. The CAN bus is clocked at 125kHz (which allows up to 500m of cable length for the Loxone Link bus).
All packages are using the extended frame format. The identifier is therefore always 29-bit (0…0x1FFFFFFF) and the data package is always 8 bytes long. Any CAN bus monitor hardware will work just fine with the Loxone Link bus.
The Loxone Link bus is a strict Master-Slave bus. The Miniserver as the master talks to the extensions, the extensions send data to the Miniserver. Extensions never talk to each other. The Miniserver can either multicast to all extensions or to specific extensions via direct commands. In the update case, it can send the update to all extensions of a certain type at the same time.
Photo with Labels of the Mainboard
Loxone UDP/HTTP Command Parser Syntax
Loxone can parse incoming data e.g. via UDP/HTTP to detect variables. I found their documentation not really good. It feels like it is almost an art to setup these parse strings, while it is actually quite simple.
The string is searched in the incoming data stream. If the full string is detected, it succeeds and returns the value (0.0 being the default). If it didn’t match the whole pattern within the stream, it resets and continues again to parse the data. Only if the whole string was parsed fully, will the matching stop.
Possible characters in the parse string
Matching individual characters
- 7-bit ASCII characters: they are taken as-is and need to match
- \\ matches a single \ (0x5C)
- \n matches a LF (0x0A)
- \r matches a CR (0x0D)
- \t matches a TAB (0x09)
- \xAB match a hex byte AB. This allows matching non-ASCII characters. \xAB would match 0xAB.
- \a match a letter (A-Z, a-z)
- \b match a TAB or space (0x09, 0x20)
- \m match a letter or digit (A-Z, a-z, 0-9)
- \d match a digit (0-9)
- \. matches any byte (= skips/ignores one byte)
Matching multiple characters
- \# match any number digits and . , or -. This should match a regular floating point number and continue after the number.
- \w match any number of letters and digits (A-Z, a-z, 0-9). This should match a word and continue after that word.
- \s123 skips/ignores 123 bytes in the incoming data stream. All digits following \s are using to build a number, the value can be really large – more than is ever needed.
Searching
-
\iXXX\i searches the string XXX and continues after that string with matching. The string can have standard UNIX control characters (\a, \b, \f, \n, \r, \t, \v plus the hex extension: \xAB).
Storing a value
The returned value is always a 64-bit floating point number, which can result in rounding issues for certain integer values.
- \1…\8 stores the following byte as part of a 64-bit binary integer result. \1 is the LSB, \8 is the MSB. Sign-extension can be applied.
- \h (hex value) stores the following ASCII-hex data (0-9, a-f, A-F) as a 32-bit MSB integer result. First invalid character ends the value. The value is returned as-is, no sign extension is applied.
- \v (value) stores a value created from ASCII characters. Any number of spaces before the values are ignored. Then the optional sign (+ or -) can follow, plus more spaces. A &nbps; is ignored after the sign as well. The number is any number of digits (0-9) followed by (, or .) plus more digits. Then an exponent (E or e) can follow plus more digits. The first character not matching the described number will end the value. The resulting number is obviously a floating point value.
- If \f (factor?) is part of the search term, the value will always be 0.0. This feels like a bug in the code and \f was probably thought to be a multiplication factor for the incoming value.
It is possible to have several \h and \v in the search pattern. Only the last one will be used. Same with \1, etc. If more than one \1 occurs, the first one will be ignored as well.
Loxone LoxCC Fileentpacker
Hi,
Here a quick Gist to uncompress a Loxone LoxCC file in Python: https://gist.github.com/sarnau/e14ff9fe081611782a3f3cb2e2c2bacd
Charge Master 2016 Serial Protocol
Charge Master 2016 Serial Protocol
The Voltcraft (Conrad) Charge Master 2016 has a completely different protocol from the CM2010 and other devices.
The USB port is still a serial port which is /dev/cu.SLAB_USBtoUART on my Mac. Every second the device sends a package with 19200 baud 8N1. I’ve written a simple python script to demonstrate reading and interpreting the data. For actual use you should add some error handling.
Only one byte in the slot header is unknown (it seems the Windows software is also not using it) and the device header has a few semi-unknown ones (the version, the temperature, etc) – only the chemical setting is actually used by the Window software.
The full source code can be found on GitHub https://github.com/sarnau/cm2016.
ELV Mobile Alerts
Mobile Alerts
This document tries to describe every detail of the Mobile Alerts sensors, which are sold by ELV in Germany, but are also available at the common suspects (Amazon, etc). Be careful buying at Amazon: certain sensors mention Mobile Alerts, but seem to be designed for the US. They are not compatible with the ELV Mobile Alerts ones!
Mobile Alerts is covering mostly climate sensors, but also contains moisture and door/window sensors plus a sound detector, which acts as a gateway for smoke sensors.
The Mobile Alerts are a version of the LaCrosse Alerts Mobile system made for the European market.
Detailed Infomation
- Mobile Alerts ELV vs LaCrosse – Difference between ELV and LaCrosse sensors
- Mobile Alerts Devices – List of all devices with technical infos
- Mobile Alerts Sensor QR Code – QR Code format found on sensors
- Mobile Alerts Website – All data send into the cloud is available via a web-interface.
- Mobile Alerts Gateway – Serial number format, LED function
- Mobile Alerts Gateway Web Interface – Web interface of the Gateway
- Mobile Alerts Gateway UDP Protocol – Find/Configure a Gateway
- Mobile Alerts Gateway REST API – Public limited REST API
- Mobile Alerts Gateway Application API – API used by iOS application to read all data from the cloud
- Mobile Alerts Gateway Upload into Cloud – binary protocol used by the Gateway to upload sensor data into the Cloud
Mobile Alerts Sensors
ELV is selling affordable weather sensors http://www.elv.de/ip-wettersensoren-system.html, which can be monitored via an iOS application. To allow that all data is transmitted via a gateway to a server, which the application can access. Sadly the protocol is not documented. I don’t think anybody has documented the way the URL has been constructed, especially the MD5 hash over the first part of it.
This sample code integrates sensors into fhem and I wrote it for Mac OS X, but it should work on Linux, too.
All information (and more) is available on GitHub as well.
To integrate the sensors into fhem, add the sensors to the iOS application and check if they show up. You need to note all sensor IDs. The ID is 6 bytes long and represented as a hexadecimal number. This same code supports sensors of type 2 (temperature only) and type 3 (temperature and humidity), which can be recognized via the first two characters of the ID.
You have to adjust the following things in the code:
- sensors = … Here you have to add all sensors IDs as strings
- downloadPath This needs to be adjusted to point to the log folder of fhem.
- vendorid You might want to change the UUID to a new one (just run uuidgen from the terminal)
Python Code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import json
import pprint
import datetime
import hashlib
import time
downloadPath = os.path.expanduser('~/fhem-5.7/log')
pp = pprint.PrettyPrinter()
sensors = set(['021122334455',...,'031122334455'])
def parseJSON(jj):
if not jj['success']:
print '#%d %s' % (jj['errorcode'],jj['errormessage'])
return
for device in jj['result']['devices']:
#pp.pprint(device)
deviceTypeId = device['devicetypeid']
deviceName = device['name']
print '%s %s [%d] #%d %s' % (device['deviceid'], deviceName, device['lowbattery'],len(device['measurements']),datetime.datetime.fromtimestamp(device['lastseen']).strftime('%Y-%m-%d %H:%M:%S'))
year = -1
month = -1
logPath = ''
for measurement in device['measurements']:
idx = measurement['idx']
transmitTime = datetime.datetime.fromtimestamp(measurement['c'])
measurementTime = datetime.datetime.fromtimestamp(measurement['ts'])
tx = measurement['tx']
mm = measurement
del mm['idx']
del mm['c']
del mm['ts']
del mm['tx']
if year != measurementTime.year or month != measurementTime.month:
if len(logPath):
f = open(logPath, "w")
for d in sorted(data):
f.write('%s %s\n' % (d,data[d]))
year = measurementTime.year
month = measurementTime.month
logPath = downloadPath + '/S_SENSOR_%s-%d-%02d.log' % (deviceName.upper().replace(' ','_'),year,month)
data = {}
try:
for line in open(logPath).readlines():
vals = line[:-1].split(' ')
data[' '.join(vals[:-1])] = vals[-1]
except:
pass
dataKey = measurementTime.strftime('%Y-%m-%d_%H:%M:%S') + ' SENSOR_' + deviceName.upper().replace(' ','_') + ' '
if deviceTypeId == 2:
data[dataKey + 'TEMP:'] = '%.1f' % (measurement['t1'])
elif deviceTypeId == 3:
data[dataKey + 'TEMP:'] = '%.1f' % (measurement['t1'])
data[dataKey + 'FEUCHTIGKEIT:'] = '%.1f' % (measurement['h'])
else:
pp.pprint(mm)
if len(logPath):
f = open(logPath, "w")
for d in sorted(data):
f.write('%s %s\n' % (d,data[d]))
import urllib
import urllib2
devicetoken = 'empty' # defaults to "empty"
vendorid = '1FB220C4-CC15-4195-97CF-8BE4FD3DAE72' # iOS vendor UUID (returned by iOS, any UUID will do). Launch uuidgen from the terminal to generate a fresh one.
phoneid = 'Unknown' # Phone ID - probably generated by the server based on the vendorid (this string can be "Unknown" and it still works)
version = '1.21' # Info.plist CFBundleShortVersionString
build = '248' # Info.plist CFBundleVersion
executable = 'Mobile Alerts' # Info.plist CFBundleExecutable
bundle = 'de.synertronixx.remotemonitor' # [[NSBundle mainBundle] bundleIdentifier]
lang = 'en' # preferred language
request = "devicetoken=%s&vendorid=%s&phoneid=%s&version=%s&build=%s&executable=%s&bundle=%s&lang=%s" % (devicetoken,vendorid,phoneid,version,build,executable,bundle,lang)
request += '&timezoneoffset=%d' % 60 # local offset to UTC time
request += '&timeampm=%s' % ('true') # 12h vs 24h clock
request += '&usecelsius=%s' % ('true') # Celcius vs Fahrenheit
request += '&usemm=%s' % ('true') # mm va in
request += '&speedunit=%d' % 0 # wind speed (0: m/s, 1: km/h, 2: mph, 3: kn)
request += '×tamp=%s' % datetime.datetime.utcnow().strftime("%s") # current UTC timestamp
requestMD5 = request + 'asdfaldfjadflxgeteeiorut0ß8vfdft34503580' # SALT for the MD5
requestMD5 = requestMD5.replace('-','')
requestMD5 = requestMD5.replace(',','')
requestMD5 = requestMD5.replace('.','')
requestMD5 = requestMD5.lower()
m = hashlib.md5()
m.update(requestMD5)
hexdig = m.hexdigest()
request += '&requesttoken=%s' % hexdig
request += '&deviceids=%s' % ','.join(sensors)
#request += '&measurementfroms=%s' % ('0,' * len(sensors))
#request += '&measurementcounts=%s' % ('50,' * len(sensors))
http_header = {
"User-Agent" : "remotemonitor/248 CFNetwork/758.2.8 Darwin/15.0.0",
"Accept-Language" : "en-us",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Host" : "www.data199.com:8080",
}
# create an urllib2 opener()
opener = urllib2.build_opener()
# create your HTTP request
req = urllib2.Request('http://www.data199.com:8080/api/v1/dashboard', request, http_header)
# submit your request
while True:
res = opener.open(req)
parseJSON(json.loads(res.read()))
print '-' * 40
time.sleep(10*60) # wait for 10 minutes
Atari ST Book: BIOS/XBIOS ROM listing
Atari ST ROM fully commented BIOS/XBIOS ROM listing for TOS 2.06, TOS 3.06 and – especially – the ST-Book ROM. I tried to collect the original comments from the Atari developers, when available. Also available on GitHub.
ROM_TOS206 EQU 1 ; regular TOS 2.06 ROM
ROM_TOS306 EQU 0 ; regular TOS 2.06 ROM
ROM_STBOOK EQU 0 ; ROM for the ST Book (variation of TOS 2.06)
osroml: bra.s reseth ; os_entry - BRA to reset handler
IF ROM_TOS306
DC.W $0306 ; os_version - TOS version number
ELSE
DC.W $0206 ; os_version - TOS version number
ENDIF
DC.L reseth ; os_reseth -> reset handler
DC.L osroml ; os_beg -> base of OS
DC.L endos ; os_end -> end BIOS/GEMDOS/VDI ram usage
DC.L reseth ; os_exec/os_rsv1 << unused, reserved >>
DC.L gem_mupb ; os_magic -> GEM memory usage param. block
IF ROM_STBOOK
DC.L $03101992 ; os_date - Date of system build ($YYYYMMDD)
ELIF ROM_TOS206
DC.L $11141991 ; os_date - Date of system build ($YYYYMMDD)
ELIF ROM_TOS306
DC.L $09241991 ; os_date - Date of system build ($YYYYMMDD)
ENDIF
DC.W $0000 ; os_conf - OS configuration bits
IF ROM_STBOOK
DC.W $186a ; os_dosdate - DOS-format date of system build
ELIF ROM_TOS206
DC.W $176e ; os_dosdate - DOS-format date of system build
ELIF ROM_TOS306
DC.W $1738 ; os_dosdate - DOS-format date of system build
ENDIF
DC.L ospool_base ; p_root -> base of OS pool
DC.L kb_shift ; pkbshift -> keyboard shift state variable
DC.L gemdos_pid ; p_run -> GEMDOS PID of current process
DC.L $000000 ; p_rsv2 << unused, reserved >>
*+
* [ROM based system]
* reseth - System reset handler
*
* Gains control of the system upon power-up reset,
* or when the RESET button is pressed,
* or after a really messy system crash.
*
*-
reseth: move #$2700,sr ; super mode, no interrupts
IF ROM_TOS306
move.w #$100,(fifo).w
move.w #0,(fifo).w
ENDIF
reset ; reset hardware
*+
* [ROM based system]
* Check for a diagnostic cartridge;
* if one is inserted, load a return address
* into A6 and jump into the cat's entry point.
*
*-
cmpi.l #$fa52235f,(cartbase).l ; is the magic number there?
bne.s reset1 ; (no)
lea reset1(pc),a6 ; a6 -> return address
jmp (cartbase+4).l ; execute diagnostic cartridge
*+
* [ROM based system]
* Is this is a warm reset, setup the memory
* controller configuration register so that
* the reset-bailout vector has something to
* stand on ....
*
*-
reset1:
IF ROM_TOS306
move.l #$808,d0
movec d0,cacr
moveq #0,d0 ; set vector base to address 0
movec d0,vbr
pmove ($e35fe4).l,tc ; 0L - disable PMMU
pmove ($e35fe4).l,tt0 ; 0L
pmove ($e35fe4).l,tt1 ; 0L
frestore ($e35fe4).l ; 0L - clear FPU
btst #0,(scu_gp1).w ; memconfig valid?
beq.s reset3 ; (no)
ENDIF
lea ret_1(pc),a6 ; load return addr
bra val_memval ; check memory configuration validity
ret_1: bne.s reset3 ; (invalid -- don't set anything up)
move.b (memcntlr).w,(memconf).w ; initialize memory controller
reset2:
IF ROM_STBOOK
move.w (STConfig).w,d0 ; Configuration on ST Book
cmp.b #$fc,d0 ; power pressed while the ST Book is closed?
beq.s reset2b ; yes => do not execute reset vector
move.w (tt_mcu+4).l,d0 ; ST Book: ???
and.b #6,d0 ; check bit 1 & 2
bne.s reset2c
reset2b: clr.l (resvalid).l
reset2c:
ENDIF
*+
* [ROM based system]
* RESET bailout vector check.
* Check to make sure we have a clean, well-bred
* bailout vector. The high byte must be zero,
* it must be even, and cannot be entirely zero.
*
*-
cmpi.l #$31415926,(resvalid).w ; is the resvalid the magic number?
bne.s reset3 ; (no)
move.l (resvector).w,d0 ; d0 = reset bailout vector
IF !ROM_TOS306
tst.b (resvector).w ; bits 24..31 must be zero
bne.s reset3 ; (they aren't, so punt)
ENDIF
btst #0,d0 ; the vector must be even
bne.s reset3 ; (it isn't, so punt)
movea.l d0,a0 ; a0 -> reset handler
lea reset2(pc),a6 ; a6 -> return address
jmp (a0) ; execute reset bailout
*+
* Initialize PSG output ports.
* Make port A and B output-only,
* initialize floppy select lines (so
* that none are selected)
*-
reset3: lea (psgsel).w,a0 ; a0 -> giselect, giwrite-2
move.b #7,(a0) ; set porta & portb to output
move.b #$c0,2(a0)
move.b #14,(a0) ; deselect disks
move.b #7,2(a0)
*+
* Determine 50hz or 60hz.
* The hardware RESETs to 60hz. Check a bit in the
* ROM configuration byte to see if we have to twiddle
* the hardware into 50hz mode.
*
*-
IF ROM_TOS306
move.b #1,(v_shf_mod).w ; Switch to 640x200x2
ELSE
btst #0,(osroml+29).l ; check bit: configured for 50hz?
beq.s notpal ; (nope -- we're good ol' NTSC)
lea ret_1b(pc),a6
bra waitvbl ; a short delay for PAL
ret_1b: move.b #2,(synmod).w ; yes -- twiddle to 50hz
notpal:
IF ROM_STBOOK
move.b #2,(v_shf_mod).w ; Switch to 640x400x1
move.b #$80,(lcdPowerControl).w ; LCD display on
tst.w (tt_mcu).l ; ST Book: ???
ENDIF
ENDIF
*+
* Initialize palette registers to
* their default values
*
*-
lea (palette).w,a1 ; a1 -> hardware reg
move.w #15,d0 ; setup 16 colors
lea colors(pc),a0 ; a0 -> table of default colors
sysic1: move.w (a0)+,(a1)+ ; copy palette assignment
dbra d0,sysic1 ; (loop for more colors)
*+
* On a ROM system, put the screen (temporarily)
* at $10000, so icon-drawing routines won't
* blow any any system variables.
*-
move.b #1,(v_bas_h).w ; set high ptr
clr.b (v_bas_m).w ; set low ptr
*+
* [ROM based system]
* Determine how much memory there is, and initialize
* the memory controller configuration register
*
*-
IF ROM_TOS306
btst #0,(scu_gp1).w ; memconfig valid?
beq.s checkmem ; (no)
ENDIF
move.b (memcntlr).w,d6 ; d6 = memory controller configuration
IF ROM_TOS306
move.b d6,(memconf).w ; setup memory controller configuration register
ENDIF
move.l (phystop).w,d5 ; d5 -> (possible) top of physical mem
lea ret_2(pc),a6 ; load return address
bra val_memval ; check if the memory configuration is valid
ret_2: beq reset4
IF ROM_STBOOK
*--- init vars + hardware:
move.b #%1010,d6 ; setup controller for 2Mb/2Mb - only valid configuration on a ST Book
move.b d6,(memconf).w ; setup memory controller
*--- write test-pattern to determine memory configuration:
move.l #$400000,d5 ; d5 -> physical top of memory (4Mb)
move.l #$6161964,d0 ; 16th June 1964...
move.l #$3251987,d1 ; 25th March 1987...
move.l d0,($300010).l
move.l d1,($300014).l
cmp.l ($300010).l,d0 ; test if 4MB is actually installed
bne.s ret_2a
cmp.l ($300014).l,d1
beq.s ret_2b
ret_2a: move.l #$100000,d5 ; d5 -> physical top of memory (1Mb)
ret_2b: lea ($8000).l,sp
ELIF ROM_TOS206
* First we try to configure the memory controller
clr.w d6
move.b #$a,(memconf).w ; default: setup controller for 2Mb/2Mb
movea.w #$8,a0
lea ($200008).l,a1 ; + 2Mb
clr.w d0
chkpatloop: move.w d0,(a0)+ ; fill 512-8 bytes with a test pattern
move.w d0,(a1)+
add.w #$fa54,d0
cmpa.w #$200,a0
bne.s chkpatloop
move.b #90,(v_bas_l).w ; wrote low byte of video address
tst.b (v_bas_m).w ; touch the middle byte (this should reset the low byte)
move.b (v_bas_l).w,d0
cmp.b #90,d0 ; low byte not reset?
bne.s chkmem1
clr.b (v_bas_l).w ; try a different low byte value
tst.w (palette).w ; touch the color palette
tst.b (v_bas_l).w ; low byte changed?
bne.s chkmem1
move.l #$40000,d7 ; 256Kb offset
bra.s chkmem1b
chkmem1: move.l #$200,d7 ; 512 byte offset
chkmem1b: move.l #$200000,d1 ; 2Mb = maximum size per bank
chkmemloop: lsr.w #2,d6 ; shift memory configuration down by a bank (bank 1 is in bits 0..1, bank 0 is in bits 2..3)
movea.l d7,a0 ; + 512/256Kb bytes
addq.l #8,a0
lea chkmem3(pc),a4
bra memchk
chkmem3: beq.s chkmem7 ; bank is not working =>
movea.l d7,a0
adda.l d7,a0 ; + 1024/512Kb byte
addq.l #8,a0
lea chkmem4(pc),a4
bra memchk
chkmem4: beq.s chkmem6 ; bank has 512Kb of memory =>
movea.w #$8,a0 ; + 0 bytes
lea chkmem5(pc),a4
bra memchk
chkmem5: bne.s chkmem7 ; bank is empty =>
addq.w #4,d6 ; 4+4 = 1000 2Mb bank size
chkmem6: addq.w #4,d6 ; 4 = 0100 512Kb bank size
chkmem7: sub.l #$200000,d1 ; - 2Mb
beq.s chkmemloop
move.b d6,(memconf).w ; set memory configuration
ELIF ROM_TOS306
checkmem:
move.w #5,d6
move.b #$a,(memconf).w
moveq #0,d0
move.l d0,($1008).w
move.l d0,($100c).w
move.l #$6161964,d0
move.l d0,($0008).w
cmp.l ($1008).w,d0
bne.s checkmem1
move.l #$4251987,d0
move.l d0,($000c).w
cmp.l ($100c).w,d0
beq.s checkmem2
checkmem1: move.w #$a,d6
checkmem2: move.b d6,(memconf).w
ENDIF
IF ROM_TOS206 || ROM_TOS306
* memory is configured, set the stack pointer
lea ($8000).l,sp
* test the memory in 128Mb blocks to find out the maximum installed
* physical memory. A failure (bus error or a memory checksum error)
* terminates the search.
movea.l (busexception).w,a4
lea chkmemx(pc),a0 ; if a bus error occurs, we are done, too
move.l a0,(busexception).w
move.w #$fb55,d3
move.l #$20000,d7 ; 128Mb is the page size for testing
movea.l d7,a0 ; 128Mb is the minimum physical memory
chkmem8: movea.l a0,a1 ; test just below the current top of memory
move.w d0,d2
moveq #$2a,d1 ; test 43 words
chkmem9: move.w d2,-(a1) ; write test pattern
add.w d3,d2
dbra d1,chkmem9
movea.l a0,a1
moveq #$2a,d1 ; check 43 words
chkmem10: cmp.w -(a1),d0 ; compare the test pattern
bne.s chkmemx ; test failed => we are done
clr.w (a1) ; erase memory after testing
add.w d3,d0
dbra d1,chkmem10
adda.l d7,a0 ; advance to the next page
bra.s chkmem8
chkmemx: suba.l d7,a0 ; the last page didn't work
move.l a0,d5 ; end of the physical memory
move.l a4,(busexception).w
ENDIF
*+
* [ROM based system]
* Clear memory from $400 to 'd5' (phystop)
*-
movea.w #etv_timer,a0 ; start at the beginning
move.l d5,d4 ; where to end
moveq #0,d0
clm_1: move.l d0,(a0)+ ; initialize all the memory
move.l d0,(a0)+
move.l d0,(a0)+
move.l d0,(a0)+
cmpa.l d4,a0 ; done?
bne.s clm_1 ; (loop for more bytes)
*+
* [ROM based system]
* Indicate that memory has successfully
* been sized and tested. Set two variables
* to magic values ...
*
*-
move.b d6,(memcntlr).w ; save configuration byte
move.l d5,(phystop).w ; save physical top-of-memory
move.l #$752019f3,(memvalid).w ; indicate memory was configured
move.l #$237698aa,(memval2).w ; ditto (paranoia variable)
move.l #$5555aaaa,(memval3).w ; ditto #2 (paranoia variable)
IF ROM_TOS306
move.l #fchkmemex,(busexception).w
move.w #0,(tt_mcu+224).l
fchkmemex:
* test the memory in 128Mb blocks to find out the maximum installed
* physical fast mem. A failure (bus error or a memory checksum error)
* terminates the search.
move.l #fchkmemx,(busexception).w ; if a bus error occurs, we are done, too
move.w #$fb55,d3
moveq #0,d0
move.l #$20000,d7 ; 128Mb is the page size for testing
movea.l #$1020000,a0 ; start address of fast mem area + 128Mb
fchkmem8: movea.l a0,a1 ; test just below the current top of memory
move.w d0,d2
moveq #$2a,d1 ; test 43 words
fchkmem9: move.w d2,-(a1) ; write test pattern
add.w d3,d2
dbra d1,fchkmem9
movea.l a0,a1
moveq #$2a,d1 ; check 43 words
fchkmem10: cmp.w -(a1),d0 ; compare the test pattern
bne.s fchkmemx ; test failed => we are done
clr.w (a1) ; erase memory after testing
add.w d3,d0
dbra d1,fchkmem10
adda.l d7,a0 ; advance to the next page
bra.s fchkmem8
fchkmemx: suba.l d7,a0 ; the last page didn't work
cmpa.l #$1000000,a0 ; it failed right at the beginning of the fast mem?
bne.s fchkmemno ; (no)
suba.l a0,a0 ; then we have no fast mem
fchkmemno: move.l a0,d5 ; end of the fast mem
move.l d5,(ramtop).w ; top of fast mem
move.l #$1357bd13,(ramvalid).w ; ramtop is valid (ramvalid == RAMMAGIC)
bset #0,(scu_gp1).w ; memconfig is valid
ELSE
clr.l (ramtop).l ; no FASTRAM available
move.l #$1357bd13,(ramvalid).w ; ramtop is valid (ramvalid == RAMMAGIC)
ENDIF
reset4: movea.l #_supstk+2048,sp ; setup supervisor stack
IF ROM_STBOOK
*+
* Initialize interrupt vectors
*
* The exception vectors are pointed to a cold boot (_coldboot)
* during startup.
*
* Trap 2 and Divide-by-zero are pointed at an rte
*
* The HBLANK, VBLANK, line 1001 [someday: line 1111), trap 13, trap 14,
* and "extended" trap vectors are initialized appropriately.
*
*-
lea _rte(pc),a3 ; a3 -> handy RTE
lea _rts(pc),a4 ; a4 -> handy RTS
*--- setup 64 vectors:
lea (_coldboot).l,a1 ; a1 -> during boot all exception trigger a coldboot, which erases all memory and resets
lea (busexception).w,a0 ; a0 -> interrupt RAM
move.w #$3f,d0 ; d0 = count
sei1: move.l a1,(a0)+ ; write vector
dbra d0,sei1 ; (loop to write more vectors)
move.l a3,(divzeroexception).w ; divide-by-zero vector -> rte
move.l a3,(Level7IRQ).w ; level #7 interrupt -> rte (power exception in a ST Book)
moveq #6,d0
lea (Level1IRQ_TTVME).w,a1
sei2: move.l #_rte,(a1)+ ; level #1 ... level #6 to RTE
dbra d0,sei2
*--- install OS interrupt vectors:
move.l #vbl,(Level4IRQ_VBL).w ; vblank handler
move.l #hbl,(Level2IRQ_HBL).w ; hblank handler
move.l a3,(trap_aesvdi).w ; (empty) trap#2 handler
move.l #trp13h,(trap_bios).w ; trap #13 handler
move.l #trp14h,(trap_xbios).w ; trap #14 handler
move.l #line1010,(lineAexception).w ;line 1010 handler
move.l a4,(etv_timer).w ; default timer-tick vector -> rts
move.l #_critich,(etv_critic).w ; default critical error handler
move.l a4,(etv_term).w ; default terminal vector -> rts
*+
* Setup the vblank deferred vector list.
* (This data structor is ugly,
* but we seem to be stuck with it).
*
*-
lea (_vbl_list).w,a0 ; a0 -> default list of vbl locs
move.l a0,(_vblqueue).w ; install ptr to them
move.w #7,d0 ; clear vbl vectors
avbl: clr.l (a0)+ ; one at a time
dbra d0,avbl
lea (tconstat).l,a0
movea.w #xconstat,a1
moveq #31,d0
tconl: move.l (a0)+,(a1)+
dbra d0,tconl
movea.l (busexception).w,a0
movea.l sp,a1
move.l #vmeinit,(busexception).w
move.b #$40,(vme_mask).w ; Enable IRQ6 from VMEBUS/MFP
move.b #$14,(sys_mask).w ; VSYNC & HSYNC enable in the VME Bus System Control Unit
vmeinit: move.l a0,(busexception).w
movea.l a1,sp
ENDIF
clr.b ($aa6).l
IF ROM_STBOOK
sf (STEFlag).l ; ST Book is not an Atari STE
ELSE
movea.l sp,a6
move.l #mwinitex,(busexception).w
clr.w (sndmactl).w
st ($a02).l ; microwire interface available
lea mwinitdata(pc),a0
move.w (a0)+,(mwmask).w
bra.s mwinit3
mwinitdata: DC.W $0ffe ; LMC 1992 mask
DC.W $09d1 ; 10-011-101000-1 = LCM - Master Volume - 0 db volume (max) - end-bit
DC.W $0aa9 ; 10-101-010100-1 = LCM - Left channel volume - 0 db volume (max) - end-bit
DC.W $0a29 ; 10-100-010100-1 = LCM - Right channel volume - 0 db volume (max) - end-bit
DC.W $090d ; 10-010-000110-1 = LCM - Trebble control - 0 db (linear) - end-bit
DC.W $088d ; 10-001-000110-1 = LCM - Bass control - 0 db (linear) - end-bit
DC.W $0803 ; 10-000-000001-1 = LCM - Mixer - DMA + YM2149 - end-bit
DC.W $0000 ; 0 = end of list
mwinit1: move.w d0,(mwdata).w
mwinit2: tst.w (mwdata).w
bne.s mwinit2
mwinit3: move.w (a0)+,d0
bne.s mwinit1
mwinitex: movea.l a6,sp
IF ROM_TOS206
move.b #90,(v_bas_l).w ; write low byte of video address
tst.b (v_bas_m).w ; access the medium byte
move.b (v_bas_l).w,d0 ; (which should reset the low byte on an STE!)
cmp.b #90,d0 ; reset?
bne.s nostedetect ; yes => STE detected
clr.b (v_bas_l).w ; clear the low byte again
tst.w (palette).w ; access the color palette
tst.b (v_bas_l).w
nostedetect:sne (STEFlag).l ; <>0 => no STE hardware available
ELSE
sf (STEFlag).l ; no STE hardware available
ENDIF
ENDIF
*+
* Clear OS bss (from 'endosbss' to 'ostext')
*-
movea.l #ostext,a1 ; a1 -> end
movea.l #endosbss,a0 ; a0 -> start
*--- common code to clear memory:
moveq #0,d0 ; quick zero
clrm_1: move.w d0,(a0)+ ; clobber a word
cmpa.l a0,a1 ; at end?
bne.s clrm_1 ; (no -- loop for more words)
IF ROM_TOS306
bsr setupPMMU ; setup PMMU translation table
ENDIF
*+
* Setup display base,
* clear display memory.
*
*-
movea.l (phystop).w,a0 ; video_base = phystop - 0x8000
IF !ROM_TOS306
suba.l #$8000,a0 ; screen size = 32kb
move.w #$7ff,d1 ; d1 = # 16-byte chunks to zero
ELSE
suba.l #$25900,a0 ; screen size = 154kb
move.w #$258f,d1 ; d1 = # 16-byte chunks to zero
ENDIF
move.l a0,(_v_bas_adr).w
move.b (_v_bas_adr+1).w,(v_bas_h).w ;load high addr
move.b (_v_bas_adr+2).w,(v_bas_m).w ;load low (really, medium) addr
moveq #0,d0 ; quick zero
clrm_2: move.l d0,(a0)+ ; zero a longword
move.l d0,(a0)+ ; zero a longword
move.l d0,(a0)+ ; zero a longword
move.l d0,(a0)+ ; zero a longword
dbra d1,clrm_2 ; (loop for more longwords)
*+
* Initialize all kinds of OS variables
*
*-
*--- OS parameters:
movea.l osroml+20(pc),a0 ; get pointer to magic
cmpi.l #$87654321,(a0) ; is the magic there?
beq.s usem
lea (osroml+8).l,a0 ; yes -- use numbers there
usem: move.l 4(a0),(end_os).w ; init end-of-OS pointer
move.l 8(a0),(exec_os).w ; init default-shell pointer
*--- Disk vectors:
move.l #_dskinit,(hdv_init).w ; initialization
move.l #_floprw,(hdv_rw).w ; read/write absolute sectors
move.l #_getbpb,(hdv_bpb).w ; media change inquiry
move.l #_mediach,(hdv_mediach).w ; get BIOS parameter block
move.l #_boot,(hdv_boot).w ; boot-from-device
move.l #_lstostat,(prv_lsto).w
move.l #_lstout,(prv_lst).w
move.l #_auxostat,(prv_auxo).w
move.l #_auxout,(prv_aux).w
move.l #_scrdmp,(scr_dump).w
*--- Randoms:
move.l (_v_bas_adr).w,(_memtop).w ;_memtop = _v_bas_ad
move.l (end_os).w,(_membot).w ; set bottom of memory (for DOS)
move.w #8,(nvbls).w ; default number of vbl queue entries
st (_fverify).w ; enable write-verify
move.w #3,(seekrate).w ; set default seek-rate
move.l #_diskbuf,(_dskbufp).w ; set pointer to disk buffer
move.w #-1,(_prtcnt).w ; initialize print-count
move.l #osroml,(_sysbase).w ; -> base of OS
move.l #savend,(savptr).w ; register-save pointer for traps 13&14
move.l #_rts,(swv_vec).w ; ignore monitor changes for now
clr.l (_drvbits).w ; remove all drives
move.l #keybellsnd,(bell_hook).w
move.l #keyclicksnd,(kcl_hook).w
bsr sysbase2ram ; copy sysbase into RAM and patch os_conf to contain os_dosdate
*--- Cookies:
lea (cookieBuffer).l,a0
move.l a0,(_p_cookies).w
move.l #'_CPU',(a0)+
moveq #0,d1 ; 68000 CPU
movea.w #illegalexception,a2
movea.l (a2),a3
movea.l sp,a1
move.l #cookieCPU,(a2)
move ccr,d0 ; first exists in the 68010
moveq #10,d1 ; 68010 CPU
extb.l d0 ; first exists in the 68020
moveq #20,d1 ; 68020 CPU
movec cacr,d0
bset #9,d0
movec d0,cacr
movec cacr,d0
bclr #9,d0
beq.s cookieCPU
moveq #30,d1 ; 68030 CPU
movec d0,cacr
cookieCPU: movea.l a1,sp
move.l a3,(a2)
move.l d1,(a0)+
sne (_longframe+1).w ; 68010+ have a longer interrupt stack frame
IF ROM_STBOOK
move.l #'_VDO',(a0)+ ; setup VDO cookie: Video hardware
move.l #$10001,(a0)+ ; 1,1 => Atari STE, ST-Book
move.l #'_MCH',(a0)+ ; setup MCH cookie: Machine type
move.l #$10001,(a0)+ ; 1,1 => Atari STE, ST-Book
move.b #$7f,d0
tst.b (STEFlag).l
bne.s cookieSTE
move.l #'_SWI',(a0)+ ; setup SWI cookie: DIP configuration switches
moveq #0,d0
move.w (STConfig).w,d0
lsr.w #8,d0
move.l d0,(a0)+ ; all DIP switches as a bit mask 0..7
cookieSTE: moveq #3,d1 ; bit 0: PSG, bit 1: 8-bit DMA
move.l #'_SND',(a0)+ ; setup SND cookie: Sound hardware
btst #7,d0 ; DIP switch 7 on?
bne.s cookieSND ; (punt)
bclr #1,d1 ; no 8-bit DMA sound
cookieSND: move.l d1,(a0)+
btst #6,d0 ; DIP switch 6 on?
bne.s cookieFDC ; (punt - no HD floppy)
move.b #8,(dsb0).l ; select HD density for drive A
move.l #'_FDC',(a0)+ ; setup FDC cookie: Floppy disk controller
move.l #$1415443,(a0)+ ; 'FDC' | (1 << 24)
cookieFDC: move.l #'_FPU',(a0)+ ; Setup FPU cookie: Type of the FPU
moveq #0,d7 ; 0 = no FPU
suba.w #$24,sp
move.l (lineFexception).w,(sp)
move.l (coprocexception).w,4(sp)
move.l #cookieFPU,(lineFexception).w
move.l #cookieFPU,(coprocexception).w
lea 8(sp),a1
movea.w #ffcp_unorderedcond,a2
move.l #cookieFPU2,d0
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
move.l (a2),(a1)+
move.l d0,(a2)+
clr.l -(sp)
movea.l sp,a2
frestore (sp)
cookieFPU2: move.l #$20000,d7 ; 0x20000 = 68881 or 68882 as co-processor (Exact type unknown)
cookieFPU: movea.l a2,sp
addq.w #4,sp
move.l (sp)+,(lineFexception).w
move.l (sp)+,(coprocexception).w
move.l (sp)+,(ffcp_unorderedcond).w
move.l (sp)+,(ffcp_inexactresult).w
move.l (sp)+,(ffcp_divzero).w
move.l (sp)+,(ffcp_underflow).w
move.l (sp)+,(ffcp_operanderror).w
move.l (sp)+,(ffcp_inexactresult).w
move.l (sp)+,(ffcp_divzero).w
move.l d7,(a0)+
movea.l (busexception).w,a1
movea.l sp,a2
move.l #cookieFPU3,(busexception).w
move.w (FPStat).w,d0
bset #0,-3(a0) ; SFP004 present
cookieFPU3: move.l a1,(busexception).w
movea.l a2,sp
ELSE
IF ROM_TOS206
tst.b (STEFlag).l ; no STE hardware available?
beq.s cookieMCH ; (correct)
move.l #'_VDO',(a0)+
clr.l (a0)+ ; 0,0 = Atari ST (260 ST, 520 ST, 1040 ST, Mega ST, ...)
move.l #'_MCH',(a0)+
clr.l (a0)+ ; 0,0 = Atari ST
bra.s cookieSWI
cookieVDO: move.l #'_VDO',(a0)+
move.l #$10000,(a0)+ ; 1,0 = STE Shifter
move.l #$10000,d0 ; x = $00 = regular STE
movea.l (busexception).w,a1
movea.l sp,a2
move.l #cookieMCH1,(busexception).w
tst.b (scu_gp1).w
move.w #$10,d0 ; x = $10 = Mega STE (with SCSI)
bra.s cookieMCH2
cookieMCH1: clr.w d0
movea.l a2,sp
move.l #cookieMCH2,(busexception).w
tst.b (ide_stat2).l
move.w #8,d0 ; x = $08 = STE with IDE (unknown machine)
cookieMCH2: move.l a1,(busexception).w
movea.l a2,sp
move.l #'_MCH',(a0)+
move.l d0,(a0)+ ; 1,x = STE (520, 1040, 2080, 4160, Mega STE, ST Book)
ELIF ROM_TOS306
move.l #'_VDO',(a0)+
move.l #$20000,(a0)+ ; 2,0 = TT Shifter
move.l #'_MCH',(a0)+
move.l #$20000,(a0)+ ; 2,0 = TT
ENDIF
cookieSWI: move.b #$7f,d0
tst.b (STEFlag).l ; no STE hardware available?
bne.s cookieSND2 ; (correct)
move.l #'_SWI',(a0)+ ; setup SWI cookie: DIP configuration switches
moveq #0,d0
move.w (STConfig).w,d0
lsr.w #8,d0
move.l d0,(a0)+
cookieSND2: moveq #3,d1 ; bit 0: PSG, bit 1: 8-bit DMA
move.l #'_SND',(a0)+ ; setup SND cookie: Sound hardware
btst #7,d0 ; DIP switch 7 on?
bne.s cookieSND3 ; (punt)
bclr #1,d1 ; no 8-bit DMA sound
cookieSND3: move.l d1,(a0)+
btst #6,d0 ; DIP switch 6 on?
bne.s cookieFDC2 ; (punt - no HD floppy)
move.b #8,(dsb0).l ; select HD density for drive A
move.l #'_FDC',(a0)+ ; setup FDC cookie: Floppy disk controller
move.l #$1415443,(a0)+ ; 'FDC' | (1 << 24)
cookieFDC2:
move.l #'_FPU',(a0)+ ; Setup FPU cookie: Type of the FPU
movea.l (lineFexception).w,a1
movea.l (coprocexception).w,a2
movea.l sp,a3
move.l #cookieFPUex,(lineFexception).w
move.l #cookieFPUex,(coprocexception).w
fmove.l d0,fp0
move.l #$20000,(a0)+ ; 6888x present
bra.s cookieFPUe2
cookieFPUex:clr.l (a0)+
cookieFPUe2:move.l a1,(lineFexception).w
move.l a2,(coprocexception).w
movea.l a3,sp
movea.l (busexception).w,a1
movea.l sp,a2
move.l #cookieFPUe3,(busexception).w
move.w (FPStat).w,d0
bset #0,-3(a0) ; SFP004 present
cookieFPUe3:move.l a1,(busexception).w
movea.l a2,sp
IF ROM_TOS306
tst.l (ramtop).w ; fast mem installed?
beq.s nfrbcookie ; (no)
move.l #'_FRB',(a0)+ ; allocate FRB cookie for DMA buffer
move.l (_membot).w,d0
move.l d0,(a0)+ ; buffer adress
add.l #$10000,d0 ; 64kb size
move.l d0,(_membot).w ; adjust memory base
move.l d0,(end_os).w
nfrbcookie:
ENDIF
ENDIF
clr.l (a0)+ ; terminate cookie list
move.l #$10,(a0)+ ; 16 available slots in the cookie store
IF !ROM_STBOOK
*+
* Initialize interrupt vectors
*
* The exception vectors are pointed to a cold boot (_coldboot)
* during startup.
*
* Trap 2 and Divide-by-zero are pointed at an rte
*
* The HBLANK, VBLANK, line 1001 [someday: line 1111), trap 13, trap 14,
* and "extended" trap vectors are initialized appropriately.
*
*-
lea _rte(pc),a3 ; a3 -> handy RTE
lea _rts(pc),a4 ; a4 -> handy RTS
*--- setup 62 vectors:
lea _term(pc),a1 ; _term -> draw number of bombs
IF !ROM_TOS306
adda.l #$2000000,a1 ; exception number in top 8 bits of the address
ENDIF
lea (busexception).w,a0 ; a0 -> interrupt RAM
move.w #$3d,d0 ; d0 = count
sei1: move.l a1,(a0)+ ; write vector
IF !ROM_TOS306
adda.l #$1000000,a1 ; increment the exception number
ENDIF
dbra d0,sei1 ; (loop to write more vectors)
move.l a3,(divzeroexception).w ; divide-by-zero vector -> rte
move.l a3,(Level7IRQ).w ; level #7 interrupt -> rte (power exception in a ST Book)
moveq #6,d0
lea (Level1IRQ_TTVME).w,a1
sei2: move.l #_rte,(a1)+ ; level #1 ... level #6 to RTE
dbra d0,sei2
*--- install OS interrupt vectors:
move.l #vbl,(Level4IRQ_VBL).w ; vblank handler
move.l #hbl,(Level2IRQ_HBL).w ; hblank handler
move.l a3,(trap_aesvdi).w ; (empty) trap#2 handler
move.l #trp13h,(trap_bios).w ; trap #13 handler
move.l #trp14h,(trap_xbios).w ; trap #14 handler
move.l #line1010,(lineAexception).w ;line 1010 handler
move.l a4,(etv_timer).w ; default timer-tick vector -> rts
move.l #_critich,(etv_critic).w ; default critical error handler
move.l a4,(etv_term).w ; default terminal vector -> rts
*+
* Setup the vblank deferred vector list.
* (This data structor is ugly,
* but we seem to be stuck with it).
*
*-
lea (_vbl_list).w,a0 ; a0 -> default list of vbl locs
move.l a0,(_vblqueue).w ; install ptr to them
move.w #7,d0 ; clear vbl vectors
avbl: clr.l (a0)+ ; one at a time
dbra d0,avbl
lea (tconstat).l,a0
movea.w #xconstat,a1
moveq #31,d0
tconl: move.l (a0)+,(a1)+
dbra d0,tconl
movea.l (busexception).w,a0
movea.l sp,a1
move.l #vmeinit,(busexception).w
move.b #$40,(vme_mask).w ; Enable IRQ6 from VMEBUS/MFP
move.b #$14,(sys_mask).w ; VSYNC & HSYNC enable in the VME Bus System Control Unit
vmeinit: move.l a0,(busexception).w
movea.l a1,sp
ENDIF
*+
* "The other half" of the BIOS handles character I/O,
* call its initialization hoook.
* (It can "never fail". This will get interesting
* if we ever to a detachable keyboard ....)
*-
bsr initmfp
move.w #$400,d0 ; Delay Mode, /50 Prescale, data = 0 (about 10us delay)
bsr mfpdelay
move.l #setikbd,-(sp)
move.w #1,-(sp)
jsr (ikbdws).l ; reset ikbd
addq.l #6,sp
move.w #$700,d0 ; Delay Mode, /200 Prescale, data = 0 (about 40us delay)
move.w #14,d1 ; 15 * 40us = 600us delay
ikbddelay: bsr mfpdelay
dbra d1,ikbddelay
*+
* Fire up %%2 cartridges
*
*-
moveq #2,d0 ; bit# = 2
bsr cartscan ; execute cartridge aps
IF ROM_STBOOK
bsr cartscan_STBOOK_EXTROM
move.b #$80,(lcdPowerControl).w ; LCD display on
move.b #$80,(lcdPowerControlShadow).w ; LCD display shadow register
moveq #2,d1 ; Switch to 640x400x1
lea setvb1(pc),a6
bra waitvbl
setvb1: move.b d1,(v_shf_mod).w ; set rez hardware register
ELIF ROM_TOS206
moveq #0,d1 ; Switch to 320x200x4
btst #7,($fffffa01).w ; monochrome monitor connected?
bne.s nomonomon ; (no)
moveq #2,d1 ; Switch to 640x400x1
nomonomon:
lea setvb1(pc),a6
bra waitvbl
setvb1: move.b d1,(v_shf_mod).w ; set rez hardware register
ELIF ROM_TOS306
moveq #4,d1 ; Switch to 640x480x4
btst #7,($fffffa01).w ; monochrome monitor connected?
bne.s nomonomon ; (no)
moveq #6,d1 ; Switch to 1280x960x1
nomonomon:
move.b d1,(shift_tt).w
ENDIF
move.b d1,(sshiftmd).w ; set rez shadow
bsr blittest
jsr ($e072aa).l ; linaA blitter/no-blitter table init
jsr (esc_init).l ; clear screen, initialize cursor
move.l #reseth,(swv_vec).w ; RESET system on monitor change
move.w #1,(vblsem).w ; enable vblank processing
*+
* [1] Fire up %%0 cartridges
* [2] Enable interrupts
* [3] Fire up %%1 cartridges
*
*-
clr.w d0 ; magic bit# = 0
bsr cartscan ; execute cartridge aps
IF ROM_STBOOK
bsr cartscan_STBOOK_EXTROM
ENDIF
move #$2300,sr ; Enable interrupts, go to IPL 3
moveq #1,d0 ; magic bit# = 1
bsr cartscan ; execute cartridge aps
IF ROM_STBOOK
bsr cartscan_STBOOK_EXTROM
move.l (_hz_200).w,d0
addq.l #3,d0
resDelayL: cmp.l (_hz_200).w,d0 ; a short delay of 15-20ms
bhi.s resDelayL
clr.b (kb_shift).l ; reset keyboard shift state
ENDIF
move.l #privinstr_exception,(privexception).w ;Emulate the MOVE SR so it works in usermode on a 68020+
bra resetcont
*+
* Flush instruction and data cache (68020+)
*-
flushCaches:move sr,-(sp)
ori #$700,sr
movec cacr,d0
or.l #$808,d0 ; CI (Clear Instruction Cache), DI (Clear Data Cache)
movec d0,cacr
move (sp)+,sr
rts
*+
* Emulate the MOVE SR,<ea> so it works in usermode on 68010+
*-
privinstr_exception:movem.l d0-d2,-(sp)
move.l a1,-(sp)
move.l a0,-(sp)
movea.l $16(sp),a0 ; a0 -> pc
move.w (a0),d0 ; d0 -> current opcode
move.w d0,d1 ; d1 -> save current opcode
and.w #$ffc0,d0
cmp.w #$40c0,d0 ; move SR,<ea>?
bne privinstr_exception_term ; (no -- punt)
move.l #$30004e71,(endosbss).w ; move.w d0,d0, nop
move.l #'NqNu',(endosbss+2*2).w ; nop, rts
move.w d1,d0
and.w #7,d0 ; <ea> register (bit 0..2)
lsl.w #8,d0
lsl.w #1,d0 ; move into bit 9..11 of the destination <ea>
or.w d0,(endosbss).w
move.w d1,d0
and.w #$38,d0 ; <ea> mode (bit 3..5)
lsl.w #3,d0 ; move into bit 6..8 of the destination <ea>
or.w d0,(endosbss).w ; generate a MOVE D0,<ea> from the MOVE SR,<ea> opcode
moveq #2,d2 ; opcode size = 2
cmp.w #$180,d0 ; <ea> == d8(An,Xn)?
beq privinstr_exception_term ; not supported -> _term
tst.w d0 ; <ea> == Dn?
beq.s privinstr_exception_dn
cmp.w #$140,d0 ; <ea> == d16(An)?
beq.s privinstr_exception_d16
cmp.w #$1c0,d0 ; <ea> == (xxx).w or (xxx).l?
bne.s privinstr_exception_other
* <ea> = (xxx).l or (xxx).w
and.w #7,d1 ; register == 0 => (xxx).w
beq.s privinstr_exception_abs16
addq.w #2,d2 ; it is (xxx).l -> opcode size += 2
move.w 4(a0),(endosbss+2*2).w ; move lower word of the absolute address
privinstr_exception_abs16:addq.w #2,d2 ; opcode size += 2
move.w 2(a0),(endosbss+2).w ; move upper word of the absolute address
bra.s privinstr_exception_ea ; -> regular destination ea
* <ea> = d16(An)
privinstr_exception_d16:addq.w #2,d2 ; opcode size += 2
move.w 2(a0),(endosbss+2).w ; move d16 offset
privinstr_exception_other:and.w #7,d1 ; d1 = An from the destination <ea>
cmp.w #7,d1 ; relative to A7?
bne.s privinstr_exception_ea ; no -> regular destination ea
* <ea> = d16(A7) or d8(A7,Dn)
* Special case because A7 has to be the USP, instead of the current SSP (in A7)
move usp,a1 ; have to use the USP, otherwise we wouldn't have gotten the exception
andi.w #$f3ff,(endosbss).w ; convert A7-relative into A1-relative destination <ea>
add.l d2,$16(sp) ; pc += opcode size
IF ROM_TOS306
bsr flushCaches
ENDIF
move.w $14(sp),d0 ; d0 = SR
jsr (endosbss).w ; execute: MOVE D0,d(A1,Dn); NOP; RTS or MOVE D0,d(A1); NOP; RTS
move a1,usp ; restore USP, but shouldn't have changed anyway
movea.l (sp)+,a0 ; restore registers
movea.l (sp)+,a1
movem.l (sp)+,d0-d2
rte ; continue execution
* <ea> = Dn
privinstr_exception_dn:add.l d2,$16(sp) ; pc += opcode size
ori.w #$10,(endosbss).w ; source ea = (A0)
IF ROM_TOS306
bsr flushCaches
ENDIF
lea 20(sp),a0 ; point to SR register to SSP
movem.l 8(sp),d0-d2 ; restore d0-d2
jsr (endosbss).w ; execute: MOVE (A0),Dn; NOP; NOP; RTS
movea.l (sp)+,a0 ; restore registers
movea.l (sp)+,a1
adda.w #$c,sp ; skip d0-d2, because they have already been restored
rte ; continue execution
privinstr_exception_ea:add.l d2,$16(sp) ; pc += opcode size
IF ROM_TOS306
bsr flushCaches
ENDIF
movea.l (sp)+,a0
movea.l (sp)+,a1
move.w 12(sp),d0 ; d0 = SR
jsr (endosbss).w ; execute: MOVE D0,<ea>; ...; RTS
movem.l (sp)+,d0-d2 ; restore registers
rte ; continue execution
privinstr_exception_term:movea.l (sp)+,a0 ; restore registers
movea.l (sp)+,a1
movem.l (sp)+,d0-d2
jmp (_term).l ; illegal instruction => _term
*+
* Load shell (if _cmdload is nonzero)
* or execute GEM in ROM
*-
resetcont: jsr (osi).l ; initialize DOS
*--- set the current system time and date
move.w (osroml+30).l,(systemDate).l ;use BIOS time as current time
jsr (readCurrentTime).l ; set current time to RTC time
beq.s notimechip ; (no RTC)
bsr readIKBDTime ; read time from the keyboard controller
swap d0
tst.b d0
beq.s notimechip ; (not valid either)
move.w d0,(systemDate).l ; update system time
swap d0
move.w d0,(systemTime).l
notimechip: clr.b (tacr).w
bclr #5,(iera).w ; disable Timer A interrupts
IF ROM_TOS306
move.l #$3111,d0
movec d0,cacr
ENDIF
movea.l #atari_image,a0 ; draw Atari Logo on the screen
movea.l (_v_bas_adr).w,a1
move.b (sshiftmd).w,d0
cmp.b #2,d0 ; 640 x 400 (bw)
beq.s drawLogobw ; (yes)
cmp.b #6,d0 ; 1280 x 960 (bw)
beq.s drawLogobw ; (yes)
IF ROM_TOS306
adda.w #4*320,a1
ELSE
adda.w #4*160,a1
ENDIF
move.w #86-1,d0 ; 86 lines
drawLogoc1: moveq #6-1,d1 ; 6 words per raster line
drawLogoc2: move.w (a0)+,d2 ; draw 4 plane Atari Logo
move.w d2,(a1)+
move.w d2,(a1)+
move.w d2,(a1)+
move.w d2,(a1)+
dbra d1,drawLogoc2
IF ROM_TOS306
adda.w #320-6*8,a1
ELSE
adda.w #160-6*8,a1
ENDIF
dbra d0,drawLogoc1
bra.s drawLogo3b
drawLogobw:
IF ROM_TOS306
adda.w #4*160,a1
ELSE
adda.w #4*80,a1
ENDIF
move.w #86-1,d0 ; 86 lines
drawLogobw2:moveq #12-1,d1 ; 12 bytes per raster line
drawLogobw3:move.b (a0)+,(a1)+ ; draw 1 plane Atari Logo
dbra d1,drawLogobw3
IF ROM_TOS306
adda.w #160-12,a1
ELSE
adda.w #80-12,a1
ENDIF
dbra d0,drawLogobw2
drawLogo3b: moveq #32+7,d7 ; y = 7
tst.b (sshiftmd).w
bne.s drawLogo4
moveq #32+12,d7 ; y = 12
drawLogo4: move.l #$30002,d6
move.w #$1b,-(sp)
move.l d6,-(sp)
trap #$d ; Bconout(2, ESC)
move.w #$59,4(sp)
move.l d6,(sp)
trap #$d ; Bconout(2, 'Y')
move.w d7,4(sp)
move.l d6,(sp)
trap #$d ; Bconout(2, ' '+0)
move.w #$20,4(sp)
move.l d6,(sp)
trap #$d ; Bconout(2, ' '+y)
addq.w #6,sp
IF !ROM_TOS306
cmpi.l #$3e80,(_hz_200).l ; system running for >80s?
bcc ptch_term ; (then no ROM CRC check)
ENDIF
*--- Check the ROM CRC
checkROM:
IF ROM_STBOOK
move.l #$3fffe,d7 ; offset to ROM CRC (last 2 bytes)
move.w #0,d6 ; number of banks = 1
ELIF ROM_TOS206
move.l #$1fffe,d7 ; offset to ROM CRC (last 2 bytes)
move.w #1,d6 ; number of banks = 2
ELIF ROM_TOS306
move.l #$1fffe,d7 ; offset to ROM CRC (last 2 bytes)
move.w #3,d6 ; number of banks = 4
ENDIF
movea.l #osroml,a5 ; ROM base address
checkROMl:
IF ROM_STBOOK
move.w #1,-(sp) ; checksum over every byte
ELIF ROM_TOS206
move.w #2,-(sp) ; checksum over every other byte
ELIF ROM_TOS306
move.w #4,-(sp) ; checksum over every 4th byte
ENDIF
move.l d7,-(sp) ; number of bytes
move.l a5,-(sp) ; buffer address
bsr calccrc
adda.w #10,sp
movea.l a5,a0
IF ROM_STBOOK
adda.l d7,a0
ELIF ROM_TOS206
adda.l #$3fffc,a0
ELIF ROM_TOS306
adda.l #$7fff8,a0
ENDIF
move.b (a0),d1 ; high byte of CRC
lsl.w #8,d1
IF ROM_STBOOK
move.b 1(a0),d1 ; low byte of CRC
ELIF ROM_TOS206
move.b 2(a0),d1 ; low byte of CRC
ELIF ROM_TOS306
move.b 4(a0),d1 ; low byte of CRC
ENDIF
cmp.w d1,d0 ; CRC identical?
bne.s checkROMbad ; (no)
addq.l #1,a5 ; next bank
dbra d6,checkROMl
bra.s ptch_term
badCRCtext: DC.B 'WARNING: BAD ROM CRC IN CHIP ',0
badCRCtextB:DC.B '.\r\n',0
checkROMbad:move.l a5,d5
pea (badCRCtext).l
move.w #9,-(sp)
trap #1
move.b #'E',d0 ; 'E' - even
btst #0,d5
beq.s checkROMbad2
move.b #'O',d0 ; 'O' - odd
checkROMbad2:move.w d0,2(sp)
move.w #2,(sp)
trap #1
IF ROM_TOS306
move.b #'E',d0 ; 'E' - even
btst #1,d5
beq.s checkROMbad3
move.b #'O',d0 ; 'O' - odd
checkROMbad3:move.w d0,2(sp)
move.w #2,(sp)
trap #1
ENDIF
move.l #badCRCtextB,2(sp)
move.w #9,(sp)
trap #1
addq.w #6,sp
addq.l #1,a5
dbra d6,checkROMl
* if no monochrome is active, holding down the alternate key forces
* to 320x200x4 instead of 640x480x4 for ST compatibility
IF ROM_TOS306
cmpi.b #6,(sshiftmd).w ; TT high?
beq.s ttlowreschk ; (yes)
move.l #$bffff,-(sp)
trap #$d ; Kbshift()
addq.w #4,sp
btst #3,d0 ; alternate pressed?
beq.s ttlowreschk ; (no)
clr.w -(sp)
pea ($ffffffff).w
pea ($ffffffff).w
move.w #5,-(sp)
trap #$e ; switch to low-res
adda.w #$c,sp
move.l #$808,d0
movec d0,cacr
ttlowreschk:
ENDIF
* During boot till this point any exception triggers a _coldboot, which
* erases the first MB and resets. From now own we point the exceptions
* to _term, which draws the bombs and terminates the currently running app
ptch_term:
IF ROM_STBOOK
move.l #$1000000,d1 ; d1 -> exception number
lea _term(pc),a0 ; new exception vector
adda.l d1,a0 ; add the exception number into the upper 8 bits (2 = bus error)
adda.l d1,a0
lea (busexception).w,a1 ; start with the bus error exception
move.w #$3f,d0
move.l #_coldboot,d2 ; old exception vector
ptch_term1: cmp.l (a1)+,d2 ; is it pointing to _coldboot?
bne.s ptch_term2 ; no -> ignore It
move.l a0,$fffc(a1) ; point it to _term
ptch_term2: adda.l d1,a0 ; increment the exception number in the upper 8 bits
dbra d0,ptch_term1 ; next vector ->
ENDIF
bsr _dskboot ; attempt to boot from disk
bsr mtest ; memory test and attempt to boot from DMA
bsr runresapps ; execute resident applications in memory
tst.w (_cmdload).l ; load shell from disk?
beq.s st_1 ; (no -- execute GEM in ROM)
bsr _auto
move.l #osroml,(_sysbase).l ; -> base of OS
pea nullenv(pc) ; null environment string
pea nullenv(pc) ; null argument string
pea cmdname(pc) ; push shell filename
clr.w -(sp) ; load-and-go flavor of execute
bra.s st_x ; exec shell ("never return")
*--- bring up GEM:
st_1: bsr _auto ; do auto-exec
IF ROM_STBOOK
bsr _auto_ROMDISK
ENDIF
move.l #osroml,(_sysbase).l ; -> base of OS
*--- kludge up an enviroment string
lea orig_env(pc),a0 ; kludge up an environment string, a0 -> original environment string
movea.l #the_env,a1 ; a1 -> place to put it
st_2: cmpi.b #'#',(a0) ; look for drive# character
bne.s st_3 ; (not it)
movea.l a1,a2 ; a2 -> place to put drive#
st_3: move.b (a0)+,(a1)+ ; copy a byte
bpl.s st_2 ; copy while not end-of-string
move.b (_bootdev).l,d0 ; compute drive#, and shove it
add.b #$41,d0 ; into the env string at the
move.b d0,(a2) ; appropriate spot
*--- kludge up an enviroment string:
pea (the_env).l ; push address of environment string
pea (nullenv).l ; no arguments
pea nullenv(pc) ; null shell name (in ROM, after all)
move.w #5,-(sp) ; createPSP flavor of exec
move.w #$4b,-(sp) ; exec function#
trap #1 ; get pointer to PSP
adda.w #14,sp ; (clean up cruft)
movea.l d0,a0 ; a0 -> PSP
move.l (exec_os).l,8(a0) ; stuff saddr of GEM in PSP
pea (the_env).l ; our environment string
move.l a0,-(sp) ; push addr of PSP
pea nullenv(pc) ; null filename
move.w #4,-(sp) ; just-go
st_x: move.w #$4b,-(sp) ; function = exec
trap #1 ; do it
adda.w #14,sp ; cleanup stack
*+
* When startup fails (or if the exec returns,
* which "cannot happen") fake a system reset:
*-
jmp (reseth).l ; back to the beginning...
*+
* Default enviroment string
* Cannot be more than 20 chars long without modifying
* the declaration for the_env,
* Any char >= $80 terminates the string (and is included in it)
* The last '#' character is replaced by the boot drive's name (A, B, ...)
*-
orig_env: DC.B 'PATH=',0 ; default pathname
DC.B '#:\',0 ; is the boot device
DC.B $00 ; terminate env string
DC.B $ff ; end of the env string (for our copy)
cmdname: DC.B 'COMMAND.PRG',0 ; shell name
gemname: DC.B 'GEM.PRG',0 ; desktop name
DC.B 0
DC.B 0
setikbd: DC.B $80,$01
*+
* _dskboot - boot (or return diagnostics)
* Passed: nothing
* Returns: D0.W = error number (if nonzero)
*-
_dskboot: moveq #3,d0 ; %%3 ap cart
bsr cartscan
IF ROM_STBOOK
bsr cartscan_STBOOK_EXTROM
ENDIF
movea.l (hdv_boot).w,a0 ; go through boot vector
jsr (a0)
tst.w d0 ; any errors?
bne.s dskb1 ; (yes -- punt)
movea.l (_dskbufp).w,a0 ; a0 -> disk buffer
jsr (a0) ; execute boot sector (it might return)
dskb1: rts ; return status
mtest: move.l #80*200,d7 ; 80s
cmp.l (_hz_200).l,d7 ; is the system running for > 80s?
bcs dmaBoot ; (no memory test)
movea.w #0,a5 ; done status = false
mtestloop: cmpa.w #0,a5 ; memtest done?
bne mtest4 ; (yes)
bsr memtest
movea.w d0,a5 ; memtest done?
beq mtest5 ; (no)
move.w #$1b,-(sp)
move.l #$30002,-(sp)
trap #$d ; Bconout(2, ESC)
move.w #'p',4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 'p') Reverse video
move.w #$1b,4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, ESC)
move.w #'w',4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 'w') Discard end of line
move.l #200,d5 ; 200 = 1s
move.w #79,d4 ; 79
tst.b (sshiftmd).l
bne.s mtest2
move.l #2*200,d5 ; 2s
moveq #39,d4 ; 39
mtest2: move.l d5,d6
move.l d4,d3
mtestsp: move.w #' ',4(sp) ; d3 + 1 spaces
move.l #$30002,(sp)
trap #$d ; Bconout(2, ' ')
dbra d4,mtestsp
move.w #$d,4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 13) CR
subq.l #1,d3
mtestsp2: move.w #' ',4(sp) ; d3 spaces
move.l #$30002,(sp)
trap #$d ; Bconout(2, ' ')
dbra d3,mtestsp2
move.w #$1b,4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, ESC)
move.w #'q',4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 'q') Normal video
addq.l #6,sp
mtest4: cmp.l (_hz_200).l,d6
bhi.s mtest5
move.w #$1b,-(sp)
move.l #$30002,-(sp)
trap #$d ; Bconout(2, ESC)
move.w #'K',4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 'K') Clear to eol
move.w #8,4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 8) Backspace
addq.w #6,sp
add.l d5,d6
mtest5: cmpa.w #0,a5 ; memtest done?
beq.s mtest6 ; (no)
cmp.l (_hz_200).l,d7
bls.s mtest8
mtest6:
IF ROM_STBOOK
move.w (tt_mcu+4).l,d0
not.w d0
and.w #$c,d0 ; check bit 2 & 3
bne.s mtest7
ENDIF
move.l #$10002,-(sp)
trap #$d ; Bconstat(2) - key pressed?
addq.l #4,sp
tst.l d0
beq mtestloop ; (no - continue)
move.l #$20002,-(sp)
trap #$d ; Bconin(2) - read key
addq.l #4,sp
mtest7: cmpa.w #0,a5 ; memtest done?
bne.s mtest8 ; (yes)
bsr memtestabort
move.l d7,(_hz_200).l
bra.s dmaBoot
mtest8: move.l d7,(_hz_200).l
move.w #$d,-(sp)
move.l #$30002,-(sp)
trap #$d ; Bconout(2, 13) CR
move.w #$1b,4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, ESC)
move.w #'K',4(sp)
move.l #$30002,(sp)
trap #$d ; Bconout(2, 'K') Clear to eol
addq.l #6,sp
*--- boot from DMA device
dmaBoot:
IF ROM_STBOOK
clr.w (the_env).l ; index to the dmaDevList
ELIF ROM_TOS206
moveq #$10,d4
ELIF ROM_TOS306
jsr (_scsisubb).l
moveq #8,d4
move.b (scu_gp1).w,d0
and.w #$f8,d0
move.w d0,(defaultBootDevice).w
bne.s dmaBoot2
pea (defaultBootDevice).w
move.w #2,-(sp)
clr.l -(sp)
jsr (_NVMaccess).l
adda.w #$a,sp
tst.w d0
beq.s dmaBoot2
ENDIF
clr.w (defaultBootDevice).w
dmaBoot2: move.w #1,d1 ; d1 -> 2 tries per device
dmaBoot3: move.w d1,-(sp)
IF ROM_STBOOK
move.w (the_env).l,d4
move.b dmaDevList(pc,d4.w),d4
ENDIF
move.w d4,-(sp) ; pdev
move.l (_dskbufp).w,-(sp) ; buf = _dskbufp
move.w #1,-(sp) ; count = 1
clr.l -(sp) ; sectnum = 0
jsr (_dmaread).l ; read first sector of this device
adda.w #12,sp
move.w (sp)+,d1
tst.l d0 ; read successful?
beq.s dmaBoot4 ; yes ->
addq.l #1,d0 ; error == time out?
dbeq d1,dmaBoot3 ; timeout or another try left? ->
bra.s dmaexecl ; try next device
dmaBoot4: movea.l (_dskbufp).w,a0
move.w #$ff,d0 ; 256 word checksum over the boot sector
moveq #0,d1
dmaBoot5: add.w (a0)+,d1
dbra d0,dmaBoot5
cmp.w #$1234,d1 ; checksum == 0x1234?
beq.s dmaexec ; execute this valid boot sector ->
dmaexecl:
IF ROM_STBOOK
move.w (the_env).l,d4
addq.w #1,d4 ; increment next device
move.w d4,(the_env).l
cmpi.b #$ff,dmaDevList(pc,d4.w) ; end of the device list?
bne.s dmaBoot2 ; no -> continue with the next one
ELSE
addq.w #1,d4
move.w d4,d0
and.w #7,d0
bne.s dmaBoot2
cmp.w #8,d4
beq.s dmaBootx
moveq #0,d4
bra.s dmaBoot2
dmaBootx:
ENDIF
rts ; no valid boot sector found -> return
IF ROM_STBOOK
dmaDevList: DC.B $10,$11,$00,$01,$02,$03,$04,$05 ;boot order of DMA devices, $ff terminates the list
DC.B $06,$07,$ff
ENDIF
dmaexec: movea.l (_dskbufp).w,a0 ; pointer to the boot sector buffer
move.l #'DMAr',d3 ; d3 -> 'DMAr' magic
move.w d4,d7 ; d4 -> pdev
asl.w #5,d7 ; d7 = 0
move.w (defaultBootDevice).w,d5 ; defaultBootDevice = 0
IF !ROM_STBOOK
move.l d4,-(sp)
ENDIF
move.l (hdv_rw).l,-(sp) ; save read sector function pointer
jsr (a0) ; execute boot sector
move.l (sp)+,d0
IF !ROM_STBOOK
move.l (sp)+,d4
ENDIF
cmp.l (hdv_rw).w,d0 ; did the read sector function change?
beq.s dmaexecl ; no -> no device driver was loaded -> continue to load boot sectors
rts ; a new device driver was loaded -> return
*+
* cartscan - scan cartridge memory for runable applications
* Passed: d0 = bit# to test in application's initialization vector
* Returns: after all applications have been examined
* Uses: a0,d0
*-
IF ROM_STBOOK
cartscan_STBOOK_EXTROM:lea (STBOOK_EXTROM).l,a0
bra.s cartscan2
ENDIF
cartscan: lea (cartbase).l,a0 ; scan cartridge memory for runable applications, a0 -> cartridge memory
cartscan2: cmpi.l #$abcdef42,(a0)+ ; correct magic number?
bne.s ca_r ; (no, so return)
ca_1: btst d0,4(a0) ; test bit in MSB of INIT address
beq.s ca_2 ; (not set, so don't execute)
movem.l d0-d7/a0-a6,-(sp) ; save everything
move.l 4(a0),d0 ; d0 -> initialization address
and.l #$ffffff,d0 ; mask out bit# in the top 8 bits of the address
movea.l d0,a0
jsr (a0) ; call cartridge application
movem.l (sp)+,d0-d7/a0-a6 ; restore everything
ca_2: tst.l (a0) ; test link address
movea.l (a0),a0 ; a0 -> next header (or NULL)
bne.s ca_1 ; loop on next header
ca_r: rts
_rts: rts
*+
* memchk - check pattern written to memory
* Passed: d1.l = offset
* a0 = base of pattern ($1f8 bytes long)
* a5 -> return address
*
* Returns: EQ: the pattern matched
* NE: the pattern didn't match
*
* Uses: d0.w, a1
* Called-by: Coldstart memory-sizing routine.
*-
memchk: adda.l d1,a0 ; a0 -> memory to check
clr.w d0 ; zap pattern seed
lea $1f8(a0),a1 ; a1 -> ending address
memchk1: cmp.w (a0)+,d0 ; match?
bne.s memchkr ; (no -- return NE)
add.w #$fa54,d0 ; yes -- bump pattern
cmpa.l a0,a1 ; matched entire pattern?
bne.s memchk1 ; (no)
memchkr: jmp (a4) ; "return" to caller
*+
* val_memval - test memory configuration validation
* Passed: a6 -> return addressd
* Returns: a5 -> 0 (quick zeropage)
* EQ: memory setup OK
* NE: memory never configured succesfully
*
*-
val_memval: cmpi.l #$752019f3,(memvalid).w ; test memory configuration validation, check first magic number
bne.s val_mr ; (mismatched -- return NE)
cmpi.l #$237698aa,(memval2).w ; check one more (for paranoia)
bne.s val_mr ; (mismatched -- return NE)
cmpi.l #$5555aaaa,(memval3).w ; check a third time (for more paranoia)
val_mr: jmp (a6) ; return EQ/NE
*+
* Default palette assignments.
* Sort of corresponding to the GSX spec.
*-
colors: DC.W $0fff ; 0 white
DC.W $0f00 ; 1 red
DC.W $00f0 ; 2 green
DC.W $0ff0 ; 3 yellow
DC.W $000f ; 4 blue
DC.W $0f0f ; 5 magenta
DC.W $00ff ; 6 cyan
DC.W $0555 ; 7 "low white"
DC.W $0333 ; 8 grey
DC.W $0f33 ; 9 light red
DC.W $03f3 ; 10 light green
DC.W $0ff3 ; 11 light yellow
DC.W $033f ; 12 light blue
DC.W $0f3f ; 13 light magenta
DC.W $03ff ; 14 light cyan
DC.W $0000 ; 15 black
*+
* hbl - force caller to IPL
* Oh-well: "Yeah, it sucks, but it works" (--lt)
*
* Note: Hacks caller's IPL to 3 (if it was 0). This is
* a kludge against fascist programs and certain
* debuggers that insist on starting processes up
* at IPL 0.
*
*-
hbl: move.w d0,-(sp) ; save d0
move.w 2(sp),d0 ; get pushed SR
and.w #$700,d0 ; strip crufty bits
bne.s hbl_r ; not IPL 0, so punt
ori.w #$300,2(sp) ; force caller to IPL 3
hbl_r: move.w (sp)+,d0 ; restore d0, back to victim
rte
*+
* vbl - vertical black interrupt handler
*
*-
vbl: addq.l #1,(_frclock).l ; bump frame clock
subq.w #1,(vblsem).l ; P(vblsem) -- vblank locked?
bmi vblret
movem.l d0-d7/a0-a6,-(sp) ; save registers
addq.l #1,(_vbclock).l ; bump unblocked-frame Clock
*------ Call deferred interrupt vectors
move.w (nvbls).l,d7 ; d7 = # of deferred vblank vectors
IF ROM_STBOOK
beq.s vbl12 ; (punt if no vectors)
ELSE
beq vbl12 ; (punt if no vectors)
ENDIF
subq.l #1,d7 ; turn into DBRA count
movea.l (_vblqueue).w,a0 ; a0 -> vectors
vbl10: movea.l (a0)+,a1 ; a1 -> deferred vector
cmpa.w #0,a1 ; if(a1 == NULL) continue;
beq.s vbl11
movem.l d7-d7/a0,-(sp) ; save registers
jsr (a1) ; call routine
movem.l (sp)+,d7-d7/a0 ; restore registers
vbl11: dbra d7,vbl10 ; loop for more vectors
IF ROM_TOS206 || ROM_TOS306
move.b (gpip).w,d1
tst.b ($a02).l ; microwire interface available?
beq.s vbl11b ; (no)
move sr,-(sp)
ori #$700,sr
vbl11a: move.b (sndmactl+1).w,d0
move.b (gpip).w,d1
btst #7,d1
sne d1
move.b (gpip).w,d2
btst #7,d2
sne d2
cmp.b d1,d2
bne.s vbl11a
cmp.b (sndmactl+1).w,d0
bne.s vbl11a
move (sp)+,sr
btst #0,d0
beq.s vbl11b
not.b d1
vbl11b:
IF ROM_TOS206
move.b (v_shf_mod).w,d0
and.b #3,d0
cmp.b #2,d0
bge.s vbl11c
ELIF ROM_TOS306
move.b (shift_tt).w,d0
and.b #7,d0
cmp.b #6,d0
beq.s vbl11c
ENDIF
btst #7,d1
bne.s vbl11e
bsr _wvbl
IF ROM_TOS206
move.b #2,d0
ELIF ROM_TOS306
move.b #6,d0
ENDIF
bra.s vbl11d
vbl11c: btst #7,d1
beq.s vbl11e
move.b (defshiftmd).w,d0
IF ROM_TOS206
cmp.b #2,d0
blt.s vbl11d
ELIF ROM_TOS306
cmp.b #6,d0
bne.s vbl11d
ENDIF
clr.b d0
vbl11d:
move.b d0,(sshiftmd).w
IF ROM_TOS206
move.b d0,(v_shf_mod).w
ELIF ROM_TOS306
move.b (shift_tt).w,d1
and.b #$f8,d1
or.b d0,d1
move.b d1,(shift_tt).w
ENDIF
movea.l (swv_vec).w,a0
jsr (a0)
vbl11e:
ENDIF
jsr (blink).l ; blink cursor
*--- reload color palettes
tst.l (colorptr).w ; reload color palettes, if(colorptr != NULL)....
beq.s vbl1
movea.l (colorptr).w,a0 ; a0 -> user's color base
lea (palette).w,a1 ; a1 -> hardware palette base
move.w #7,d1 ; d0 = count
vbl2: move.l (a0)+,(a1)+ ; load a palette
dbra d1,vbl2 ; ...and repeat
clr.l (colorptr).w ; zap colorptr
*--- reload display base register
vbl1: tst.l (screenpt).w ; reload display base register, if(screenpt == NULL) don't
beq.s vbl5
move.l (screenpt).w,(_v_bas_adr).w ;set OS variable
move.b (_v_bas_adr+2).w,(v_bas_m).w ;load "middle" pointer
move.b (_v_bas_adr+1).w,(v_bas_h).w ;load "high" pointer
move.b (_v_bas_adr+3).w,(v_bas_l).w ;load "low" pointer
*------ Floppy drive-select timeout
vbl5: bsr _flopvbl ; (no args)
vbl12: tst.w (_prtcnt).w ; monitor screen dump flag - printscreen active?
bne.s no_print ; no
*+
* printScreen
*
* We re-enable vblanks here, until the printScreen finishes -
*
*-
bsr _dumpit ; dump screen
no_print: movem.l (sp)+,d0-d7/a0-a6 ; restore registers & return (and a handy RTE)
vblret: addq.w #1,(vblsem).l ; V(vblsem) [release vblank]
_rte: rte
*+
* wvbl - wait for next vblank
* Passed: nothing
* Returns: at beginning of next vblank
* Uses: D0
*-
wvbl: move sr,-(sp) ; save psw
andi #$fbff,sr ; enable vbl interrupts
move.l (_frclock).l,d0 ; d0 = frame clock
wvbl1: cmp.l (_frclock).l,d0 ; wait for clock to change
beq.s wvbl1
move (sp)+,sr ; then restore psw & return
rts
*+
* _critic - critical error handler binding for C
* Falls-into: _critich
* (screwy way to save two bytes....)
*
*-
crit_err: move.l (etv_critic).l,-(sp) ; jump through critic vector
*+
* _critich - default critical error handler
* Loads -1 into D0 and returns
*
*-
_critich: moveq #-1,d0 ; default return value = ERROR
rts ; return to trap invoker
*+
* trp13h - GEMDOS BIOS trap handler (trap 13)
* trp14h - Atari BIOS extensions (trap 14)
* traph - trap handler
*
* On the stack:
* From super- From user
* visor mode: mode:
* ----------- ------------
* N(sp) args N(usp) args
* 6(sp) func# 6(usp) func#
* 2(sp) ret 2(ssp) ret
* (sp) SR (ssp) SR
*
* Returns: anything in D0
* Uses: d0-d2/a0-a2
* Keeps: C registers
*
* Notes BIOS traps are re-entrant to 'nlevels' (declared near the
* beginning of this file). Attempts to recurse more than
* 'nlevels' will probably result in a crash.
*
* BIOS calls may be made from user mode. (This differs from
* the current GEMDOS spec, which states that BIOS traps are
* available from supervisor mode only).
*
*-
trp14h: lea trp14tab(pc),a0 ; a0 -> trap14 jump table
bra.s traph
trp13h: lea trp13tab(pc),a0 ; a0 -> trap13 jump table
traph: movea.l (savptr).l,a1 ; a1 - registers save area
move.w (sp)+,d0 ; pop SR and save it
move.w d0,-(a1) ; (need in D0 for user-mode test)
move.l (sp)+,-(a1) ; save return addr
IF !ROM_TOS306
tst.w (_longframe).w
beq.s traph2
ENDIF
tst.w (sp)+
traph2: movem.l d3-d7/a3-a7,-(a1) ; save C registers + super stack
move.l a1,(savptr).l ; update save-area pointer
* make sure we have the right stack, call function:
btst #13,d0 ; was in user mode?
bne.s b_supr ; (was in super: use super stack)
move usp,sp ; use user stack
b_supr: move.w (sp)+,d0 ; get function#
cmp.w (a0)+,d0 ; out of range?
bge.s b_exit ; (yes, so punt)
move.w d0,d1
lsl.w #2,d1 ; turn D0 into longword index
move.l (a0,d1.w),d1 ; get pointer to function handler
IF ROM_TOS306
bclr #0,d1 ; clear the indirect but set the old status in Z
movea.l d1,a0
beq.s b_1 ; points to code
ELSE
movea.l d1,a0 ; (quick and dirty test-for-negative)
bpl.s b_1 ; points to code
ENDIF
movea.l (a0),a0 ; indirect through RAM...
b_1: suba.l a5,a5 ; a5 -> zero page
jsr (a0) ; call BIOS function
* restore registers, cleanup stack and return:
b_exit: movea.l (savptr).l,a1 ; a1 -> register save area
movem.l (a1)+,d3-d7/a3-a7 ; restore C registers + super stack
IF !ROM_TOS306
tst.w (_longframe).w
beq.s trph3
ENDIF
clr.w -(sp)
trph3: move.l (a1)+,-(sp) ; push return address
move.w (a1)+,-(sp) ; push old SR
move.l a1,(savptr).l ; update save-pointer
rte ; return to caller
*------ jump table for GEMDOS functions:
trp13tab: DC.W $000c ; number of entries in jump table
DC.L _get_mpb
DC.L bconstat
DC.L bconin
DC.L bconout
IF ROM_TOS306
DC.L hdv_rw | 1
ELSE
DC.L hdv_rw
ENDIF
DC.L _setexc
DC.L _tickcal
IF ROM_TOS306
DC.L hdv_bpb | 1
ELSE
DC.L hdv_bpb
ENDIF
DC.L bcostat
IF ROM_TOS306
DC.L hdv_mediach | 1
ELSE
DC.L hdv_mediach
ENDIF
DC.L _drvmap
DC.L _shift
*------ jump table for Atari BIOS extensions:
trp14tab:
IF ROM_TOS306
DC.W $0060 ; number of entry points
ELSE
DC.W $0041 ; number of entry points
ENDIF
DC.L initmous
DC.L _rts
DC.L _physbase
DC.L _logbase
DC.L _getrez
DC.L _setscreen
DC.L _setpalette
DC.L _setcolor
DC.L _floprd
DC.L _flopwr
DC.L _flopfmt
DC.L _getdsb
DC.L midiws
DC.L mfpint
DC.L iorec
DC.L rsconf
DC.L keytrans
DC.L _rand
DC.L _proto_bt
DC.L _flopver
DC.L _dumpit
DC.L _cursconf
DC.L settime
DC.L gettime
DC.L bioskeys
DC.L ikbdws
DC.L jdisint
DC.L jenabint
DC.L giaccess
DC.L offgibit
DC.L ongibit
DC.L xbtimer
DC.L dosound
DC.L setprt
DC.L ikbdvecs
DC.L kbrate
DC.L _prtblk
DC.L wvbl
DC.L supexec
DC.L puntaes
DC.L _rts
DC.L floprate
DC.L _dmaread
DC.L _dmawrite
DC.L _bconmap
DC.L _rts
IF ROM_TOS306
DC.L _NVMaccess
ELSE
DC.L _rts
ENDIF
IF ROM_STBOOK || ROM_TOS206
DC.L _Waketime
ELSE
DC.L _rts
ENDIF
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L blitmode
IF ROM_TOS306
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _EsetShift
DC.L _EgetShift
DC.L _EsetBank
DC.L _EsetColor
DC.L _EsetPalette
DC.L _EgetPalette
DC.L _EsetGrey
DC.L _EsetSmear
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
* this routine seems to be unused?
_e00fae: btst #0,1(sp) ; read?
bne.s _hdv_rw_ptc ; (no) => no patch necessary, write doesn't need to clear the cache
move.l #_hdv_rw_ptt,(sp) ; patch return address
_hdv_rw_ptc:movea.l (hdv_rw).w,a0
jmp (a0)
_hdv_rw_ptt:move.l d0,-(sp)
bsr _flush_cache ; flush the CPU cache
move.l (sp)+,d0
jmp (b_exit).l ; exit trap
ENDIF
*+
* supexec - execute some code in supervisor mode
*
*-
supexec: movea.l 4(sp),a0 ; a0 -> code
jmp (a0) ; execute it
*+
* Character device I/O
*
* No check is made for "bogus" device numbers. A wierd device
* number will result in a crash.
*
*-
bconstat: lea (xconstat).w,a0 ; a0 -> stat table
moveq #0,d1
bra.s chsw
bconin: lea (xconin).w,a0 ; a0 -> input table
moveq #4,d1
bra.s chsw
bcostat: lea (xcostat).w,a0 ; a0 -> ostat table
moveq #8,d1
bra.s chsw
bconout: lea (xconout).w,a0 ; a0 -> output table
moveq #$c,d1
chsw: move.w 4(sp),d0 ; get device number
cmp.w #5,d0
bls.s chsw2
subq.l #6,d0
cmp.w (bdevcount).w,d0
bcc.s chsw1
movea.l (bdevtabptr).w,a0
asl.w #3,d0
adda.w d0,a0
add.w d0,d0
adda.w d0,a0
movea.l (a0,d1.w),a0
jmp (a0)
chsw1: moveq #0,d0
rts
chsw2: lsl.w #2,d0 ; turn into longword index
movea.l (a0,d0.w),a0 ; get address of handler
jmp (a0) ; jump to it
*+
* Jump tables for
* 0 - lst: (printer)
* 1 - aux: (rs232)
* 2 - con: (screen)
* 3 - Atari midi
* 4 - Atari keyboard (output only)
* 5 - raw console output (bypass vt52 pressure cooker)
*
* No range checking is performed. If a bogus device number
* is passed to the BIOS' character I/O handler, the system
* will crash to become funky duex.
*
*-
tconstat: DC.L _rts
DC.L auxistat
DC.L constat
DC.L midstat
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
tconin: DC.L _lstin
DC.L auxin
DC.L conin
DC.L midin
DC.L _rts
DC.L _rts
DC.L _rts
DC.L _rts
tcostat: DC.L _lstostat
DC.L _auxostat
DC.L conoutst
DC.L ikbdost
DC.L midiost
DC.L _rts
DC.L _rts
DC.L _rts
tconout: DC.L _lstout
DC.L _auxout
DC.L conout
DC.L midiwc
DC.L ikbdwc
DC.L _asc_out
DC.L _rts
DC.L _rts
*+
* _drvmap - return "active drive" bit vector
* Passed: nothing
* Returns: D0.L = a bit vector of live (rwabs'able) block device
*
*-
_drvmap: move.l (_drvbits).w,d0
rts
*+
* _shift - get/set keyboard shift state
* Synopsis: LONG _shift(bits)
* WORD bits
*
* Returns: D0.B = shift/alt/ctl/shift' bits
*
* Note: Since the shift bits are changed at interrupt
* level, any set from a get of the shift state
* must be done as a critical section.
*
*-
_shift: moveq #0,d0
move.b (kb_shift).w,d0
move.w 4(sp),d1
bmi.s shifr
move.b d1,(kb_shift).w
shifr: rts
*+
* _get_mpb - return initial memory parameter block
* Synopsis: _get_mpb(mpb)
* MPB *mpb;
*
* Returns: The properly initialized MPB.
* The MPB points to an MD somewhere in BSS. The MD /must/
* be in RAM since DOS will modify it.
*-
_get_mpb: movea.l 4(sp),a0 ; a0 -> MPB
lea (themd).w,a1 ; a1 -> MD
*--- initialize MPB:
move.l a1,(a0) ; mp_mfl = &themd;
clr.l 4(a0) ; mp_mal = NULL;
clr.l 8(a0) ; mp_rover = NULL;
*---- initialize MD:
clr.l (a1) ; m_link = NULL;
move.l (_membot).w,4(a1) ; m_start = _membot;
move.l (_memtop).w,d0 ; m_length = _memtop - membot;
sub.l (_membot).w,d0
move.l d0,8(a1)
clr.l $c(a1) ; m_own = NULL;
IF ROM_TOS306
cmpi.l #$1357bd13,(ramvalid).l ; fast mem available?
bne.s _get_mpbret ; (no)
cmpi.l #$1000000,(ramtop).l ; fast mem installed?
bls.s _get_mpbret ; (no)
lea ($0a02).w,a2 ; themd_tt
move.l a2,(a1) ; themd.m_link = &themd_tt
clr.l (a2) ; m_link = NULL
move.l #$1000001,4(a2) ; m_start = base address of fast mem | 1
move.l (ramtop).l,d0
sub.l #$1000000,d0
move.l d0,8(a2) ; m_length = size of fast mem
clr.l $c(a2) ; m_own = NULL
_get_mpbret:
ENDIF
rts
*+
* _setexc - set exception vector
* Synopsis: setexc(vecno, addr)
* If 'addr' < 0, the vector is not set.
*
* Extended vectors ($100 though $107) are located in the
* first eight longwords of BSS, at $400. This is for
* convienience -- they could really be located anywhere.
*
* Returns: D0.L = original vector value
*
*-
_setexc: move.w 4(sp),d0 ; d0 = vector #
lsl.w #2,d0 ; turn into longword index
suba.l a0,a0
lea (a0,d0.w),a0 ; a0 -> vector
move.l (a0),d0 ; d0 = current vector address
move.l 6(sp),d1 ; d1 = what_to_change_it_to
bmi.s setex1 ; punt if (d1 < 0)
move.l d1,(a0) ; set vector address
setex1: rts
*+
* _tickcal - return system timer calibration value (in ms)
*
*-
_tickcal: moveq #0,d0 ; cast to unsigned longword
move.w (_timr_ms).w,d0 ; get calibration
rts
*+
* _physbase - get physical display base
*
*-
_physbase: moveq #0,d0 ; cleanup pointer-to-be
move.b (v_bas_h).w,d0 ; load and shift bits 16..23
lsl.w #8,d0
move.b (v_bas_m).w,d0 ; load and shift bits 8..15
lsl.l #8,d0
tst.b (STEFlag).l
bne.s _physbase2
move.b (v_bas_l).w,d0
_physbase2: rts ; return pointer in d0
*+
* _logbase -get logical display base
*
*-
_logbase: move.l (_v_bas_adr).w,d0 ; set software shadow
rts
*+
* _getrez - get current screen rez
*-
_getrez: moveq #0,d0 ; cleanup dirty bits
IF ROM_STBOOK
move.b (sshiftmd).w,d0 ; get screen rezolution
and.b #3,d0 ; strip garbage bits
ELIF ROM_TOS206
move.b (v_shf_mod).w,d0 ; get screen rezolution
and.b #3,d0 ; strip garbage bits
ELIF ROM_TOS306
move.b (shift_tt).w,d0
and.b #7,d0
ENDIF
rts ; return rez
*+
* _setscreen - set screen location(s), rez
* _setscreen(logicalLoc, physicalLoc, rez)
* LONG logicalLoc, physicalLoc;
* WORD rez;
*
*-
*--- set logical location:
_setscreen: tst.l 4(sp) ; if(logloc < 0) then ignore it
bmi.s f5a
move.l 4(sp),(_v_bas_adr).w ; set software pointer from logloc
*--- set physical location:
f5a: tst.l 8(sp) ; if(physloc < 0) then ignore it
bmi.s f5b
move.b 9(sp),(v_bas_h).w ; set bits 16..23 of hardware pointer
move.b $a(sp),(v_bas_m).w ; set bits 8..15 of hardware pointer
move.b $b(sp),(v_bas_l).w ; set bits 0..7 of hardware pointer
*--- set screen resolution (clears the screen, clobbers the cursor):
f5b: tst.w $c(sp) ; if(rez < 0) then ignore it
bmi.s f5r
bsr _wvbl ; wait for start of vertical-blank
move.b $d(sp),(sshiftmd).w ; set software shadow
IF ROM_TOS306
move.b (shift_tt).w,d0
and.b #$f8,d0
or.b $d(sp),d0
move.b d0,(shift_tt).w
ELSE
move.b (sshiftmd).w,(v_shf_mod).w ;set hardware location
ENDIF
clr.w (vblsem).w ; disable vblank processing
jsr (esc_init).l ; re-initialize glass tty routines
move.w #1,(vblsem).w ; re-enable vblanks
f5r: rts
*+
* _setpalette - set palette (on next vblank)
* _setpalette(LONG palettePtr)
*
*-
_setpalette:move.l 4(sp),(colorptr).w ; set software pointer
rts ; (updated by vbl handler)
*+
* _setcolor - set single color, return old color
* _setcolor(WORD colorNum, WORD colorValue)
*
*-
_setcolor: move.w 4(sp),d1 ; get color number
add.w d1,d1 ; turn into word index
and.w #$1f,d1 ; force color range (prevent buserr)
lea (palette).w,a0 ; a0 -> base of palette memory
move.w (a0,d1.w),d0 ; return old color
tst.b (STEFlag).l
beq.s _setc0a
and.w #$777,d0 ; mask dirty bits
bra.s _setc0b
_setc0a: and.w #$fff,d0
_setc0b: tst.w 6(sp) ; if new color is <0, don't set it
bmi.s _setc1 ; (punt)
move.w 6(sp),(a0,d1.w) ; set new color
_setc1: rts
*+
* puntaes - throw-away AES, restart the system
* Passed: nothing
* Uses: everything
* Returns: if AES was already thrown away
*
*-
puntaes: movea.l osroml+20(pc),a0 ; get pointer to magic
cmpi.l #$87654321,(a0) ; is the magic still there?
bne.s paes1 ; no -- just return
cmpa.l (phystop).w,a0 ; is it in ROM?
bge.s paes1 ; yes -- we can't do anything about it
clr.l (a0) ; clobber AES!
bra reseth ; restart the system
paes1: rts
*+
* _term - terminate current process
* Called-by: Uncaught traps (bus errors, and so on)
* Saves: processor state (in a bailout area)
*
*-
_term:
IF !ROM_TOS306
jsr (savp_2).l ; stack PC
savp_2:
ENDIF
move.l (sp)+,(proc_pc).w ; save bogus PC + exception number
movem.l d0-d7/a0-a7,(proc_regs).w ; common registers
IF ROM_TOS306
move.l 2(sp),(proc_pc).w
move.w 6(sp),d0
and.w #$fff,d0
asr.w #2,d0
move.b d0,(proc_pc).w
ENDIF
move usp,a0 ; save USP
move.l a0,(proc_usp).w
moveq #15,d0 ; save 16 words off top of
lea (proc_stk).w,a0 ; the stack (enough for
movea.l sp,a1 ; any possible 680000 exception)
savp_1: move.w (a1)+,(a0)+ ; save a word
dbra d0,savp_1
move.l #$12345678,(proc_lives).w ; set magic number (procdump lives)
*--- draw an appropriate number of 'shrooms on the screen:
moveq #0,d1
move.b (proc_pc).w,d1
subq.w #1,d1 ; 2 for bus error, 3 for address, etc.
bsr.s do_shroom
move.l #savend,(savptr).w ; clobber BIOS top level
move.w #-1,-(sp) ; "error" return condition
move.w #$4c,-(sp) ; GEMDOS function #$4C
trap #1 ; "terminate process"
bra reseth ; on return, reset system
*+
* do_shroom - draw little mushroom clouds on the screen
* Passed: d1.w = #shrooms to draw (DBRA count)
* Returns: some shrooms on display
* Uses: d0-d7/a0-a2
*
* Discussion: The graphics ain't all that great. And this is silly.
*
*-
do_shroom: move.b (sshiftmd).w,d7
IF ROM_TOS306
and.w #7,d7
ELSE
and.w #3,d7
ENDIF
add.w d7,d7 ; d7 = rez index
moveq #0,d0
move.b (v_bas_h).w,d0
lsl.w #8,d0
move.b (v_bas_m).w,d0
lsl.l #8,d0
tst.b (STEFlag).l
bne.s dmste
move.b (v_bas_l).w,d0
dmste: movea.l d0,a0 ; a0 -> base of mem to draw at
cmp.w #6,d7
blt.s dm00
adda.l #768*100,a0
bra.s dm01
dm00: adda.w #160*100,a0
dm01: lea (mushroom).l,a1 ; a1 -> source from
move.w #15,d6 ; d6 = scanline count
dm0: move.w d1,d2 ; d3 = # to draw on this line
movea.l a0,a2 ; safe ptr to beg of line
dm1: move.w mcount(pc,d7.w),d5 ; d5 = #words to replicate
dm2: move.w (a1),(a0)+ ; draw a word
dbra d5,dm2 ; (complete single shroom)
dbra d2,dm1 ; another, on the same line
addq.w #2,a1 ; next source word
adda.w mwidth(pc,d7.w),a2 ; next dest line
movea.l a2,a0
dbra d6,dm0 ; (loop for next line)
moveq #30-1,d7
dm3: bsr wvbl
dbra d7,dm3
rts ; byebye
mcount: DC.W $0003,$0001,$0000,$0000
DC.W $0003,$0000,$0000,$0007
mwidth: DC.W $00a0,$00a0,$0050,$0000
DC.W $0140,$0000,$00a0,$0140
*+
* _fastcpy - "fast" 512-byte copy
* Synopsis: fastcpy(src, dest)
*
* Used by _rwabs to fake disk DMA to odd addresses. Therefore,
* disk I/O on odd addresses is very slow. Lose, lose.
*
*-
_fastcpy: movea.l 4(sp),a0 ; a0 -> src
movea.l 8(sp),a1 ; a1 -> dest
move.w #$3f,d0 ; d0 = move count (64*8 = 512)
fast1: move.b (a0)+,(a1)+ ; copy 8 bytes at a time
move.b (a0)+,(a1)+ ; to minimize loop overhead
move.b (a0)+,(a1)+
move.b (a0)+,(a1)+
move.b (a0)+,(a1)+
move.b (a0)+,(a1)+
move.b (a0)+,(a1)+
move.b (a0)+,(a1)+
dbra d0,fast1
rts
*+
* Go through hard-disk initialization vector
*
*-
_hinit: move.l (hdv_init).l,-(sp)
rts
autopath: DC.B '\AUTO\*.PRG',0
DC.W $1234,$5678,$9abc,$def0
*+
* _auto - exec auto-startup files in the appropriate subdirectory
* _auto1 - exec (with filename args)
* Passed: a0 -> full filespec (pathname)
* a1 -> filename part of filespec
* _drvbits: bit vector of active drives
* _bootdev: contains device to exec from
*
* Returns: nothing
*
* Note: If _drvbits%%_bootdev is zero, _auto simply quits (since
* the device isn't active....)
*
* Uses: everything
*-
_auto: move.l #$bffff,-(sp)
trap #$d
addq.l #4,sp
btst #2,d0 ; control key pressed?
bne.s autoq ; (then ignore the AUTO folders)
IF !ROM_TOS306
move.l (_drvbits).w,d0 ; d0 - active dev vector
move.w (_bootdev).l,d1 ; d1 - dev# to exec from
btst d1,d0 ; is the dev alive?
beq.s autoq ; (no -- so punt)
ENDIF
lea autopath(pc),a0 ; -> path
lea autopath+6(pc),a1 ; -> filename
_auto1: move.l (sp)+,(autoret).l ; return addr (used by execlr)
move.l a0,(pathname).w ; setup filename/pathname ptrs
move.l a1,(filename).w
IF ROM_TOS306
move.l (_drvbits).w,d0 ; d0 - active dev vector
move.w (_bootdev).l,d1 ; d1 - dev# to exec from
btst d1,d0 ; is the dev alive?
beq.s autoq ; (no -- so punt)
ENDIF
lea nullenv(pc),a0 ; a0 -> \0\0
move.l a0,-(sp) ; null enviorment
move.l a0,-(sp) ; null command tail
move.l a0,-(sp) ; null shell name
move.w #5,-(sp) ; Create-PSP subfunction
move.w #$4b,-(sp) ; exec function#
trap #1 ; do DOS CALL
adda.w #16,sp
movea.l d0,a0 ; a0 -> PSP
move.l #fauto,8(a0) ; initial PC -> autoexec prog
move.l a3,-(sp) ; null enviroment
move.l d0,-(sp) ; -> PSP
move.l a3,-(sp) ; null shell name
move.w #4,-(sp) ; just-go
move.w #$4b,-(sp) ; function = exec
trap #1 ; do it
adda.w #16,sp ; cleanup stack & goodbye
move.l (autoret).l,-(sp)
autoq: rts
*+
* ST Book has another ROMDISK in the 2nd 256kb of the ROM
* which is mapped as drive 'P'.
*-
IF ROM_STBOOK
_auto_ROMDISK:lea autopathROM(pc),a0 ; -> path
lea autopathROM+8(pc),a1 ; -> filename
bra.s _auto1
autopathROM:DC.B 'P:\AUTO\*.PRG',0
DC.W $1234,$5678,$9abc,$def0
ENDIF
*+
* fauto - exec'd by _auto to do autostartup
*
* Passed: pathname -> path part of filespec
* filename -> file path of filespec
*
*-
fauto: clr.l -(sp) ; get into super mode
move.w #$20,-(sp)
trap #1
addq.w #6,sp ; cleanup
movea.l d0,a4 ; a4 -> saved super stack
*---- free up some memory
movea.l 4(sp),a6 ; a6 -> base page
lea $100(a6),sp ; sp -> new, safer addr
move.l #$100,-(sp) ; keep $100 (just the basepage)
move.l a6,-(sp) ; -> start of mem to keep
clr.w -(sp) ; junk word
move.w #$4a,-(sp) ; setblock(...)
trap #1
addq.w #6,sp
tst.w d0
bne.s au_dn ; punt on error
move.w #7,-(sp) ; find r/o+hidden+system files
move.l (pathname).l,-(sp) ; -> filename (on input)
move.w #$4e,-(sp) ; searchFirst
moveq #8,d7 ; d7 = cleanup amount
au1: pea (autodma).l ; setup DTA (for search)
move.w #$1a,-(sp)
trap #1 ; search first/search next
addq.w #6,sp
trap #1
adda.w d7,sp ; cleanup stack
tst.w d0 ; test for match
bne.s au_dn ; (no match -- quit)
*--- construct filename from path and the name we just found:
movea.l (pathname).l,a0 ; copy pathname
movea.l (filename).l,a2 ; a2 -> end+1 of pathname
lea (autoname).l,a1
au3: move.b (a0)+,(a1)+ ; copy path part of name
cmpa.l a0,a2 ; finished?
bne.s au3 ; (no)
lea (autodma+30).l,a0 ; copy fname to end of pathname
au2: move.b (a0)+,(a1)+
bne.s au2
pea nullenv(pc) ; null enviroment
pea nullenv(pc) ; no command tail
pea (autoname).l ; -> file to exec
clr.w -(sp) ; load-and-go
move.w #$4b,-(sp) ; exec(...)
trap #1
adda.w #16,sp
moveq #2,d7 ; reset cleanup amount
move.w #$4f,-(sp) ; searchNext
bra.s au1
*+
* The first GEMDOS process can never terminate.
* This is not a good feature.
* Kludge around it - re-initialize the stack
* and return to the guy who called us to begin with.
*
*-
au_dn: lea (_supstk+2048).l,sp ; setup supervisor stack
move.l (autoret).l,-(sp) ; get return addr
rts ; just jump there ...
*+
* _dumpit: dump screen
*
*-
_dumpit: movea.l (scr_dump).l,a0
jsr (a0)
move.w #-1,(_prtcnt).l
rts
*+
* _scrdmp - printScreen(), font-end to _prtblk()
* Passed: nothing
* Returns: nothing
* Uses: everything
*-
_scrdmp: move.l (_v_bas_adr).w,(p_blkptr).w ;-> screen mem
clr.w (p_offset).w ; offset = 0
clr.w d0
move.b (sshiftmd).w,d0 ; get w & h
move.w d0,(p_srcres).w
add.w d0,d0
lea reztab(pc),a0
move.w (a0,d0.w),(p_width).w ; set display width, height
move.w 6(a0,d0.w),(p_height).w
clr.w (p_left).w ; left = right = 0
clr.w (p_right).w
move.l #$ffff8240,(p_colpal).w ; -> hardware palettes
clr.w (p_masks).w ; default mask ptr
* draft or final mode
move.w (pconfig).w,d1 ; p_dstres = pconfig%%3
lsr.w #3,d1
and.w #1,d1
move.w d1,(p_dstres).w
* printer or rs232 port
move.w (pconfig).w,d1 ; p_port = pconfig%%4
move.w d1,d0
lsr.w #4,d0
and.w #1,d0
move.w d0,(p_port).w
* select printer flavor
and.w #7,d1 ; p_type = ptype[pconfig & 7]
move.b ptype(pc,d1.w),d0
move.w d0,(p_type).l
* do it
pea (p_blkptr).w ; -> beginning of parameter area
move.w #1,(_prtcnt).w
bsr _prtblk ; print it (finally)
move.w #-1,(_prtcnt).l
addq.w #4,sp ; cleanup stack
rts ; and return
*---- screen resolution table (pixel) for printScreen
reztab: DC.W $0140,$0280,$0280,$00c8
DC.W $00c8,$0190
*--- printer flavors (based on low 3 bits of pconfig)
ptype: DC.B $00,$02,$01,$ff,$03,$ff,$ff,$ff
*--- what it is:
mushroom: DC.W $0600,$2900,$0080,$4840
DC.W $11f0,$01f0,$07fc,$0ffe
DC.W $0dfe,$1fff,$1fef,$0fee
DC.W $0fde,$07fc,$03f8,$00e0
*+
* waitvbl - wait for the beam inside the vertical blank area
*-
IF ROM_TOS306
waitvbl: clr.b (tbcr).w
clr.b (tbdr).w
move.b #8,(tbcr).w
waitvbl2: tst.b (tbdr).w
beq.s waitvbl2
jmp (a6)
ELSE
waitvbl: lea (tbdr).w,a0 ; a0 -> timer B data register
lea (tbcr).w,a1 ; a1 -> timer B control register
bclr #0,(iera).w ; disable IRQ of timer B
moveq #1,d4 ; wait for the timer to expire
clr.b (a1) ; stop timer B
move.b #$f0,(a0) ; event every 240 scan lines
move.b #8,(a1) ; timer b: event count mode (HBL)
waitvbl2: cmp.b (a0),d4 ; wait for HBL 239 scan lines to pass
bne.s waitvbl2
waitvbl3: move.b (a0),d4
move.w #615,d3 ; wait till we are inside the vbl area
waitvbl4: cmp.b (a0),d4
bne.s waitvbl3
dbra d3,waitvbl4
move.b #$10,(a1) ; timer b: reset
jmp (a6)
ENDIF
_wvbl: bra wvbl
*+
* runresapps - search address space for 512-byte resident code chunks
*
*-
runresapps: movea.l (phystop).l,a0 ; start at the top of the address space
runresappsl:suba.w #$200,a0
cmpa.w #$400,a0 ; reach the lower bottom?
bls.s runresappsx ; (bail out)
cmpi.l #$12123456,(a0) ; check for magic
bne.s runresappsl ; (no magic -> next block)
cmpa.l 4(a0),a0 ; second long is equal the base address of the block?
bne.s runresappsl ; (no -> next block)
clr.w d0
movea.l a0,a1
move.w #$ff,d1 ; checksum over 256 words
runresappsc:add.w (a1)+,d0 ; checksum over the block
dbra d1,runresappsc
cmp.w #$5678,d0 ; magic checksum?
bne.s runresappsl ; (no -> next block)
move.l a0,-(sp) ; save current address
jsr 8(a0) ; call code block
movea.l (sp)+,a0 ; continue with next block
bra.s runresappsl
runresappsx:rts
gettime: lea (readRTCTime).l,a3
lea (readIKBDTime).l,a4
bra.s timecode
settime: move.w 4(sp),(systemDate).l
move.w 6(sp),(systemTime).l
lea (writeRTCTime).l,a3
lea (writeIKBDTime).l,a4
timecode: bsr checkRTC
bcc.s timecodertc
movea.l a4,a3
timecodertc:jmp (a3)
*+
* sysbase2ram - copy sysbase into memory and patch it
*-
sysbase2ram:lea osroml(pc),a0
lea (ramsysbase).l,a1
moveq #$2f,d0
sysbase2raml:move.b (a0,d0.w),(a1,d0.w) ; copy 48 bytes into memory
dbra d0,sysbase2raml
move.w sysbase2ramjmp(pc),$fffa(a1) ;copy JMP instruction just before
move.l 4(a1),$fffc(a1) ; copy ROM reseth into this JMP
move.w sysbase2rambra(pc),(a1) ; patch BRA at the beginning of sysbase copy to hit the JMP
move.w $1e(a1),$1c(a1) ; copy os_date into os_conf
move.l a1,(_sysbase).l ; set new sysbase pointer
rts
sysbase2ramjmp:jmp ($0000).w ; destination address is patched to reseth address
sysbase2rambra:bra.s sysbase2ramjmp ; branch onto the JMP
*+
* blitmode - enabled/disable/test the blitter
*
* Returns: d0.w - bit 0 - blitter enabled
* bit 1 - blitter available
*-
blitmode: bsr.s blittest ; test if blitter is installed
move.w d0,d4 ; d4 = blitter status
move.w d0,d5
lsr.w #1,d5
or.w #$fffe,d5 ; d5 = blitter enable mask
jsr ($e0732a).l
move.w d0,d3 ; d3 = blitter active
move.w 4(sp),d0 ; new blitter status
bmi.s blitmodex ; <0 just return the status
and.w d5,d0 ; mask blitter status out
or.w d4,d0 ; or'd blitter available status
jsr ($e07302).l ; disable/enable blitter
blitmodex: move.w d3,d0
rts
*+
* blittest - test if the blitter is installed
*-
blittest: move sr,d1
move.w #0,d0 ; d0 = 0 -> blitter not installed
suba.l a0,a0
movea.l sp,a2
ori #$700,sr
movea.l 8(a0),a1
move.l #blittestx,8(a0)
tst.w $8a00(a0)
moveq #2,d0 ; d0 = 2 -> blitter installed
blittestx: move.l a1,8(a0)
move d1,sr
movea.l a2,sp
rts
*+
* delay for a little bit via timer a in the mfp
*-
mfpdelay: bsr.s mfpdelaysetup
IF !ROM_TOS306
mfpdelayl: btst #5,(ipra).w ; did timer a fire?
beq.s mfpdelayl ; wait more if not
clr.b (tacr).w ; stop timer a
ELSE
mfpdelayl: btst #5,(iprb_TT).w ; did timer a fire?
beq.s mfpdelayl ; wait more if not
ENDIF
rts
*+
* setup timer a in the mfp
*-
mfpdelaysetup:
IF !ROM_TOS306
move sr,-(sp)
ori #$700,sr
clr.b (tacr).w ; timer A control register
bclr #5,(iera).w ; disable timer A
move.b #$df,(ipra).w ; clear a pending timer A interrupt
bclr #5,(imra).w ; mask the timer A interrupt
bset #5,(iera).w ; enable timer A
move (sp)+,sr
move.b d0,(tadr).w ; set timer a data register
ror.w #8,d0
move.b d0,(tacr).w ; set timer a control register
rol.w #8,d0
ELSE
movem.w d0-d1,-(sp)
move sr,-(sp)
ori #$700,sr
move.b (tcdcr_TT).w,d1
and.b #$f,d1
move.b d1,(tcdcr_TT).w
bclr #5,(ierb_TT).w
move.b #$df,(iprb_TT).w
bclr #5,(imrb_TT).w
bset #5,(ierb_TT).w
move.b d0,(tcdr_TT).w
lsr.w #4,d0
and.b #$f0,d0
or.b d0,d1
move.b d1,(tcdcr_TT).w
move (sp)+,sr
movem.w (sp)+,d0-d1
rts
ENDIF
rts
memclear: movea.l 4(sp),a0 ; a0 -> start address of the block
movea.l 8(sp),a1 ; a1 -> end address of the block
movem.l d3-d7/a3,-(sp)
moveq #0,d1
moveq #0,d2
moveq #0,d3
moveq #0,d4
moveq #0,d5
moveq #0,d6
moveq #0,d7
movea.w d7,a3
move.l a0,d0
btst #0,d0
beq.s memclear1
move.b d1,(a0)+ ; clear a single byte to even-align
memclear1: move.l a1,d0
sub.l a0,d0
and.l #$ffffff00,d0
beq.s memclearb
lea (a0,d0.l),a0
movea.l a0,a2
lsr.l #8,d0
memclear256:movem.l d1-d7/a3,-(a2) ; clear 256 bytes
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
movem.l d1-d7/a3,-(a2)
subq.l #1,d0
bne.s memclear256
memclearb: cmpa.l a0,a1 ; end address reached?
beq.s memcleare
move.b d1,(a0)+ ; clear a single byte
bra.s memclearb
memcleare: movem.l (sp)+,d3-d7/a3
rts
*+
* setup PMMU translation table
*-
IF ROM_TOS306
setupPMMU: lea ($700).l,a0
lea ($e363f0).l,a1 ; translation table
move.w #$3f,d0 ; 64 descriptors
setupPMMUl: move.l (a1)+,(a0)+
dbra d0,setupPMMUl
* PMMU Table:
* $x.......
* $700: $00000742,$10000001,$20000001,$30000001,$40000001,$50000001,$60000001,$70000001,
* $720: $80000041,$90000041,$A0000041,$B0000041,$C0000041,$D0000041,$E0000041,$00000782,
* $0.......
* $740: $000007C2,$01000001,$02000001,$03000001,$04000001,$05000001,$06000001,$07000001,
* $760: $08000001,$09000001,$0A000001,$0B000001,$0C000001,$0D000001,$0E000001,$0F000001,
* $Fx......
* $780: $00000041,$01000041,$02000041,$03000041,$04000041,$05000041,$06000041,$07000041,
* $7a0: $08000041,$09000041,$0A000041,$0B000041,$0C000041,$0D000041,$0E000041,$000007C2,
* $00...... or $FF......
* $7c0: $00000001,$00100001,$00200001,$00300001,$00400001,$00500001,$00600001,$00700001,
* $7e0: $00800001,$00900001,$00A00001,$00B00001,$00C00001,$00D00001,$00E00001,$00F00041,
* The purpose of the PMMU is to disable caching in the area of the address space that doesn't contain ram:
* $00F00000..$00FFFFFF
* $80000000..$EFFFFFFF
* $F0000000..$FEFFFFFF
* $FFF00000..$FFFFFFFF
pmove ($e364f0).l,crp ; CPU Root Pointer: $80000002,$00000700: 4 byte descriptor, at $700, lower limit: $00000
pmove ($e364f8).l,tc ; Translation Control Register: $80F04445: MMU enabled, pagesize = 15, bits: 4,4,4,5
pmove ($e364fc).l,tt0 ; Transparent Translation Register 0: $017E8107
pmove ($e36500).l,tt1 ; Transparent Translation Register 1: $807E8507
rts
ENDIF
*+
* calccrc - calc a crc
*-
calccrc: movea.l 4(sp),a0 ; a0 -> buffer start
move.l 8(sp),d2 ; d2 -> number of bytes
movea.w $c(sp),a1 ; a1 -> step offset through the buffer
clr.w d0
clr.w d1
clr.w d3
lea (crcTable).l,a2
calccrcl: move.w d0,d1
lsl.w #8,d0
lsr.w #8,d1
move.b (a0),d3
adda.l a1,a0
eor.b d3,d1
add.w d1,d1
move.w (a2,d1.w),d4
eor.w d4,d0
subq.l #1,d2
bne.s calccrcl
rts
IF ROM_TOS306
_EsetShift: bsr _wvbl
moveq #0,d0
move.w (shift_tt).w,-(sp)
move.w 6(sp),(shift_tt).w
move.w (shift_tt).w,d0
and.w #7,d0
move.b d0,(sshiftmd).w
clr.w (vblsem).w
jsr (esc_init).l
move.w #1,(vblsem).w
move.w (sp)+,d0
rts
_EgetShift: moveq #0,d0
move.w (shift_tt).w,d0
rts
_EsetBank: moveq #0,d0
move.w (shift_tt).w,d0
and.w #$f,d0
tst.w 4(sp)
bmi.s _EsetBankx
move.b 5(sp),(shift_tt+1).w
_EsetBankx: rts
_EsetColor: moveq #0,d0
lea (TT_col).w,a0
move.w 4(sp),d0
and.w #$ff,d0
add.w d0,d0
adda.w d0,a0
move.w (a0),d0
and.w #$fff,d0
move.w 6(sp),d1
bmi.s _EsetColorx
move.w d1,(a0)
_EsetColorx:rts
_EsetPalette:move.w 4(sp),d0
and.w #$ff,d0
movea.w d0,a0
adda.w a0,a0
sub.w #$100,d0
neg.w d0
move.w 6(sp),d1
cmp.w d0,d1
ble.s _EsetPal2
move.w d0,d1
_EsetPal2: movea.l 8(sp),a1
lea $8400(a0),a0
bra.s _EsetPal4
_EsetPal3: move.w (a1)+,(a0)+
_EsetPal4: dbra d1,_EsetPal3
rts
_EgetPalette:move.w 4(sp),d0
and.w #$ff,d0
movea.w d0,a0
adda.w a0,a0
sub.w #$100,d0
neg.w d0
move.w 6(sp),d1
cmp.w d0,d1
ble.s _EgetPal2
move.w d0,d1
_EgetPal2: movea.l 8(sp),a1
lea $8400(a0),a0
bra.s _EgetPal4
_EgetPal3: move.w (a0)+,(a1)+
_EgetPal4: dbra d1,_EgetPal3
rts
_EsetGrey: moveq #0,d0
move.b (shift_tt).w,d1
move.b d1,d0
lsr.b #4,d0
and.b #1,d0
bclr #4,d1
tst.w 4(sp)
beq.s _EsetGrey2
bmi.s _EsetGreyr
bset #4,d1
_EsetGrey2: move.b d1,(shift_tt).w
_EsetGreyr: rts
_EsetSmear: moveq #0,d0
move.b (shift_tt).w,d1
move.b d1,d0
add.b d0,d0
subx.w d0,d0
neg.w d0
bclr #7,d1
tst.w 4(sp)
beq.s _EsetSmear2
bmi.s _EsetSmearr
bset #7,d1
_EsetSmear2:move.b d1,(shift_tt).w
_EsetSmearr:rts
ENDIF
**************************************************************************
* DMAREAD.S from the AHDI source *
**************************************************************************
*+
* dmaread() - read from a DMA device.
* dmawrite() - write to a DMA device.
*
* LONG sectnum $4(sp).l
* WORD count $8(sp).w
* BYTE *buf; $a(sp).l $b(sp)=high $c(sp)=mid $d(sp)=low
* WORD pdev; $e(sp).w
*
* Returns 0 if successful.
* Returns a negative number if failure:
* EUNDEV (-15L) if unknown device (bad dev number)
* EREADF (-11L) if read failure
* EWRITF (-10L) if write failure
* (-1L) if timed-out
*
* Comments:
* ACSI, SCSI and IDE-AT drives are supported.
*-
_dmaread: move.b #0,(rwflag).l ; rwflag = 0 => a read
move.w #$08,d1 ; d1 = opcode for READ
bra.s dmarw
_dmawrite: move.b #1,(rwflag).l ; rwflag = 1 => a write
move.w #$0a,d1 ; d1 = opcode for WRITE
dmarw: cmpi.w #$10,$e(sp) ; unit# > IDE-AT unit# 16?
bhi undev ; if so, return unknow device
cmpi.w #$f,$e(sp) ; an IDE-AT unit?
bhi.s ide0 ; if so, talk IDE-AT
* else talk ACSI or SCSI
lea (_cmdblk).l,a0 ; a0 -> beginning of command block
move.b d1,(a0)+ ; byte 0 = opcode
move.b 5(sp),(a0)+ ; byte 1 = msb of logical block addr
move.b 6(sp),(a0)+ ; byte 2 = logical block addr
move.b 7(sp),(a0)+ ; byte 3 = lsb of logical block addr
move.b 9(sp),(a0)+ ; byte 4 = transfer length (in blocks)
clr.b (a0) ; byte 5 = control byte
move.w $e(sp),d0 ; d0 = physical unit number
IF ROM_TOS306
moveq #0,d1
move.w 8(sp),d1
lsl.l #8,d1
add.l d1,d1
ENDIF
moveq #6,d2 ; d2 = length of command
movea.l $a(sp),a0 ; a0 = buffer
tst.b (rwflag).l ; read or write?
bne.s rw0 ; it's a write
bsr _dorcmd ; send a receive data command
bra rw1
rw0: bsr wracsi ; send a write data command
bra rw1 ; branch will take place for non-IDE drives
*
* Before doing anything with the IDE bus, make sure it's there.
* (The label noide is before at just because it fits better.)
*
noide: movea.l a1,sp ; got the bus error on IDEASR;
move.l a0,(busexception).w ; restore vector & sp, return EUNDEV
bra undev
ide0: movea.l (busexception).w,a0
movea.l sp,a1
move.l #noide,(busexception).w
tst.b (ide_stat2).l ; read a register to probe for IDE bus
movea.l a1,sp ; hey! no bus error!
move.l a0,(busexception).w ; restore vector & sp and continue.
move.b $f(sp),d0 ; IDE unit#
bsr _iderdy ; wait for drive to be ready
IF ROM_TOS306
beq undev ; if drive never gets ready, return with unknown device else
ELSE
beq.s undev ; if drive never gets ready, return with unknown device else
ENDIF
moveq #2,d0 ; D_IDENTIFY delay required by Conner drives
add.l (_hz_200).w,d0 ; between power on and identify()
ide1: cmp.l (_hz_200).w,d0
bcc.s ide1
pea (sbuf).l ; scratch buffer for drive parameters
move.w $12(sp),-(sp) ; IDE unit#
bsr _identify ; identify()
addq.w #6,sp ; clean up stack
tst.w d0 ; successful?
IF ROM_TOS306
bmi rwend ; if timed-out, return
ELSE
bmi.s rwend ; if timed-out, return
ENDIF
bne.s rw2 ; if error, return with error code else can do read or write
moveq #2,d0 ; D_IDENTIFY delay required by Conner drives
add.l (_hz_200).w,d0 ; between identify() and r/w
ide4: cmp.l (_hz_200).w,d0
bcc.s ide4
IF ROM_TOS306
lea (sbuf).l,a0
move.w $c(a0),-(sp) ; # of sectors per track
move.w 6(a0),-(sp) ; # of heads
move.w $12(sp),-(sp) ; physical unit #
bsr _initdevpar
addq.w #6,sp
tst.w d0
bmi.s rwend
bne.s rw2
lea (sbuf).l,a0
ELSE
pea (sbuf).l ; beginning of _identify() data
bsr _gcparm ; get drive current parameters
addq.w #4,sp ; clean up stack
ENDIF
move.w $e(sp),-(sp) ; physical unit #
move.l $c(sp),-(sp) ; buffer
move.w $e(sp),-(sp) ; count
move.l $c(sp),-(sp) ; logical block address
IF ROM_TOS306
move.w $c(a0),-(sp) ; # of sectors per track
move.w 6(a0),-(sp) ; # of heads
ELSE
move.w d2,-(sp) ; # sectors per track
move.w d1,-(sp) ; # data heads
ENDIF
tst.b (rwflag).l ; read or write?
bne.s ide2 ; (write)
bsr _ideread ; read sectors
bra.s ide3
ide2: bsr _idewrite ; write sectors
ide3: adda.w #16,sp ; clean up stack
rw1:
IF ROM_TOS306
move sr,-(sp)
ori #$700,sr
movec cacr,d1
ori.w #$808,d1
movec d1,cacr
move (sp)+,sr
ENDIF
tst.w d0 ; successful?
ble.s rwend ; if no error or timed-out, return else
rw2: moveq #-11,d0 ; assume it's a read error EREADF
tst.b (rwflag).l ; read or write?
beq.s rwend ; if read, done
moveq #-10,d0 ; else, it's a write error EWRITF
rwend: rts
undev: moveq #-15,d0 ; EUNDEV
bra.s rwend
*+
* dorcmd() - send a command which will receive data from the target
*
* Passed:
* d0.w = physical unit number
* d2.w = command length (NCMD or LCMD)
* a0.l = buffer address
*-
_dorcmd:
IF ROM_TOS306
cmp.w #7,d0
bls.s _dorcmd2
bsr _rcvscsi
rts
_dorcmd2: bsr _rcvacsi ; it's an ACSI device
ELSE
bsr.s _rcvacsi ; it's an ACSI device
ENDIF
rts
*+
* dowcmd() - send a command which will write data to the target
*
* Passed:
* d0.w = physical unit number
* d2.w = command length (NCMD or LCMD)
* a0.l = buffer address
*-
wracsi:
IF ROM_TOS306
cmp.w #7,d0
bls.s wracsi2
bsr _wrtscsi
rts
wracsi2:
ENDIF
bsr _wrtacsi ; it's an ACSI device
rts
*+
* LONG _qdone() - Wait for command byte handshake
* LONG _fdone() - Wait for operation complete
* Passed: nothing
*
* Returns: EQ: no time-out
* MI: time-out condition
*
* Uses: D0
*
*-
_fdone: move.l #600,d0 ; ACLTMOUT
bra.s qd0
_qdone: moveq #20,d0 ; ACSTMOUT
qd0:
IF ROM_TOS306
move.l d0,-(sp) ; save timeout value
moveq #2,d0 ; busy-wait delay for slow ACSI
add.l (_hz_200).w,d0 ; minimum 20 microsec.
sdelay: cmp.l (_hz_200).w,d0
bge.s sdelay
move.l (sp)+,d0 ; restore timeout value
ENDIF
add.l (_hz_200).w,d0
qd1: cmp.l (_hz_200).w,d0 ; time-out?
bcs.s qdq ; (i give up, return NE)
btst #5,(gpip).w ; interrupt?
bne.s qd1 ; (not yet)
moveq #0,d0 ; return EQ (no time-out)
rts
qdq: moveq #-1,d0
rts
*+
* Wait for end of SASI command
*
* Passed: d1 value to be written to wdl
*
* Returns: EQ: success (error code in D0.W)
* MI: time-out (-1 in D0.W)
* NE: failure (SASI error code in D0.W)
*
* Uses: d0
*-
_endcmd: bsr.s _fdone ; wait for operation complete
bmi.s endce ; (timed-out, so complain)
IF ROM_TOS306
move sr,-(sp)
ori #$700,sr
movec cacr,d0
ori.w #$800,d0
movec d0,cacr
move (sp)+,sr
ENDIF
move.w d1,(fifo).w
move.w (diskctl).w,d0 ; get the result
and.w #$ff,d0 ; (clean it up) if non-0 should do a RequestSense command to learn more
endce: move.l (_hz_200).w,(lastacstm).l ; update controller last accessed time
addq.l #2,(lastacstm).l ; lastacstm = _hz_200 + 2;
rts
*+
* Handle command time-out;
* Unlock DMA chip and return completion status;
*-
_hdone: move.w #$80,(fifo).w ; Landon's code seems to presume we put $80 there
IF ROM_TOS306
clr.w (flock).w ; NOW, signal that we are done
ELSE
sf (flock).w ; NOW, signal that we are done
ENDIF
rts
*+
* delay()
* 5 - 10ms kludge delay for message byte sent back by controller.
*-
_delay: move.l d0,-(sp) ; preserve d0
move.l (lastacstm).l,d0 ; d0 = controller last accessed time
wait: cmp.l (_hz_200).w,d0 ; while (_hz_200 <= lastacstm)
bcc.s wait ; wait()
move.l (sp)+,d0 ; restore d0
rts
*+
* rcvacsi() - send a ACSI command which receives data from target.
*
* Passed:
* d0.w = physical unit number
* d2.w = command length (NCMD or LCMD)
* a0.l = buffer address
*-
_rcvacsi: movea.l (busexception).w,a1 ; check to see if the ACSI bus actually exists! Needed for prototype PADs
movea.l sp,a2
move.l #noacsi,(busexception).w
tst.w (fifo).w ; harmless or bus erorr
move.l a1,(busexception).w ; no bus error - restore & continue
movea.l a2,sp
st (flock).w ; lock FIFO
bsr.s _delay ; delay if necessary
movea.w #diskctl,a1 ; a1 = pointer to DMA chip
bsr setadma ; set DMA pointer
move.w #$190,2(a1) ; WDL ; toggle DMA chip to direction
bsr rstdelay ; delay
move.w #$90,2(a1) ; WDL ; for receiving data
bsr rstdelay ; delay
bsr setacnt ; set DMA count
lea (_cmdblk).l,a0 ; a0 = address of command block
moveq #0,d1 ; direction of DMA is IN
bsr.s sblkacsi ; send the command block
raend: bra.s _hdone ; cleanup after IRQ
*+
* wrtacsi() - send an ACSI command which will write data to the target
*
* Passed:
* d0.w = physical unit number
* d2.w = command length (NCMD or LCMD)
* a0.l = buffer address
*-
*
_wrtacsi: movea.l (busexception).w,a1 ; check to see if the ACSI bus actually exists! Needed for prototype PADs
movea.l sp,a2
move.l #noacsi,(busexception).w
tst.w (diskctl).w ; harmless or bus erorr
move.l a1,(busexception).w ; no bus error - restore & continue
movea.l a2,sp
st (flock).w ; lock FIFO
bsr.s _delay
movea.w #diskctl,a1 ; a1 = pointer to DMA chip
bsr.s setadma ; set DMA pointer
move.w #$90,2(a1) ; WDL ; toggle DMA chip for "send"
bsr rstdelay ; delay
move.w #$190,2(a1) ; WDL
IF ROM_TOS306
bsr rstdelay ; delay
ELSE
bsr.s rstdelay ; delay
ENDIF
bsr.s setacnt ; set DMA count
move.l #$100,d1 ; d1 = direction of DMA is OUT
bsr.s sblkacsi ; send the command block
waend: bra _hdone ; cleanup after IRQ
* You get to this label if there is a bus error when probing for
* the ACSI bus: it's meant for prototype PADs, which don't
* have an ACSI DMA chip.
noacsi: moveq #-15,d0 ; return EUNDEV
movea.l a2,sp
move.l a1,(busexception).w
rts
*+
* sblkacsi() - send command block
*
* Passed:
* d0.w = physical unit number
* d1.l = direction of DMA ($0000 for IN or $0100 for OUT)
* d2.w = command length (NCMD or LCMD)
* a1.l = pointer to DMA chip
*
* Returns:
* d0.l = 0 if successful
* d0.l = -1 if time-out
*
* Trashes:
* d0, d1, d2, a2
*-
sblkacsi: move.b #$88,d1 ; next byte is the opcode
move.w d1,2(a1) ; WDL
move.b #$8a,d1 ; following bytes are operands
lea (_cmdblk).l,a2 ; a2 = address of command block
* integrate unit # into cmd blk
lsl.b #5,d0 ; shift unit number into place
or.b d0,(a2) ; first command byte = unit # | opcode
* control byte is sent seperately
subq.w #2,d2 ; and dbra likes one less
sa1: swap d1 ; d1.hw = operand
move.b (a2)+,d1 ; d1.lw = tells controller next byte
swap d1 ; is an operand
move.l d1,(a1) ; WDCWDL
bsr _qdone
bmi.s sbaend ; if time-out, returns
dbra d2,sa1 ; else send rest of command block
move.w d1,2(a1) ; WDL - get ready to send control byte
move.b #0,d1 ; signal sending control byte
swap d1 ; d1.hw = control byte
move.b (a2),d1 ; d1.lw = tells controller it's end
swap d1 ; of command
move.l d1,(a1) ; send it
move.b #$8a,d1 ; d1 = wdl value
bsr _endcmd ; wait for command completion
sbaend: rts ; heading home
*+
* setadma() - set the ACSI DMA pointer
*
* Passed:
* a0.l = buffer address
*-
setadma: move.l a0,-(sp) ; move it on stack
move.b 3(sp),(dmalow).w ; set low-byte of address
move.b 2(sp),(dmamid).w ; set mid-byte of address
move.b 1(sp),(dmahigh).w ; set high-byte of address
addq.l #4,sp ; clean up stack
rts
*+
* setacnt() - set the ACSI DMA counter
*
* Passed:
* a1.l = pointer to DMA chip
*-
setacnt:
IF ROM_TOS306
move.l d1,-(sp)
lsr.l #8,d1
add.l d1,d1
move.w d1,(a1)
move.l (sp)+,d1
ELSE
move.w #$ff,(a1) ; set the ACSI DMA counter to 255 512-byte-blocks
ENDIF
rts
* After talking to the DMA chip in a way that may reset it,
* we need a 8 8Mhz clocks (ie. 1 microsec) delay, before we can
* talk to the chip again.
rstdelay: tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
tst.b (gpip).w
rts
IF ROM_TOS306
_rcvscsi: move.l a0,-(sp)
andi.w #7,d0
bsr.s _scsicmd
movea.l (sp)+,a0
tst.w d0
bmi.s _rcvscsir
movea.l a0,a1
movea.w #s_data,a2
move.b #1,(s_tcr).w
move.b (s_inircv).w,d0
_rcvscsil: bsr _scsidelay250ms
bsr _scsiwait
bmi.s _rcvscsir
btst #3,$a(a2)
beq _scsiaction
move.b (a2),(a1)+
bsr _scsidone
bra.s _rcvscsil
_rcvscsir: rts
_wrtscsi: andi.w #7,d0
move.l a0,-(sp)
move.w d2,-(sp)
bsr.s _scsicmd
move.w (sp)+,d2
movea.l (sp)+,a0
tst.w d0
bmi.s _wrtscsir
movea.l a0,a1
movea.w #s_data,a2
move.b #0,(s_tcr).w
move.b (s_inircv).w,d0
_wrtscsil: bsr _scsidelay250ms
bsr _scsiwait
bmi.s _wrtscsir
btst #3,$a(a2)
beq _scsiaction
move.b (a1)+,(a2)
bsr _scsidone
bra.s _wrtscsil
_wrtscsir: rts
_scsicmd: move.l d2,-(sp)
move.w d0,-(sp)
bsr.s _scsido
addq.w #2,sp
move.l (sp)+,d2
tst.w d0
bmi.s _scsicmdr
move.b #2,(s_tcr).w
move.b #1,(s_icr).w
lea ($a96).l,a1
subq.w #1,d2
bsr _scsidelay250ms
_scsicmdl: move.b (a1)+,d0
bsr _scsisuba
tst.w d0
bmi.s _scsicmdr
dbra d2,_scsicmdl
moveq #0,d0
_scsicmdr: rts
_scsido: bsr _scsidelay250ms
_scsidow: btst #6,(s_idstat).w
beq.s _scsido2
cmp.l (a0),d1
bhi.s _scsidow
bra.s _scsidoerr
_scsido2: move.b #0,(s_tcr).w
move.b #0,(s_idstat).w
move.b #$c,(s_icr).w
clr.w d0
move.w 4(sp),d1
bset d1,d0
move.b d0,(s_data).w
move.b #$d,(s_icr).w
andi.b #$fe,(s_mode).w
andi.b #$f7,(s_icr).w
bsr _scsidelay250ms
_scsidow2: btst #6,(s_idstat).w
bne.s _scsidook
cmp.l (a0),d1
bhi.s _scsidow2
_scsidoerr: moveq #-1,d0
bra.s _scsidowr
_scsidook: clr.w d0
_scsidowr: move.b #0,(s_icr).w
rts
_scsiaction:bsr _scsidelay250ms
move.b #3,(s_tcr).w
move.b (s_inircv).w,d0
bsr.s _scsiwait
bmi.s _scsiactionr
moveq #0,d0
move.b (s_data).w,d0
bsr _scsidelay250ms
move.l d0,-(sp)
bsr.s _scsidone
tst.w d0
beq.s _scsiaction3
_scsiaction2:addq.l #4,sp
bra.s _scsiactionr
_scsiaction3:bsr.s _scsidelay250ms
bsr.s _scsiwait
bmi.s _scsiaction2
move.b (s_data).w,d0
bsr.s _scsidone
tst.w d0
bmi.s _scsiaction2
move.l (sp)+,d0
_scsiactionr:rts
_scsiwait: btst #5,(s_idstat).w
bne.s _scsiwaitok
cmp.l (a0),d1
bhi.s _scsiwait
moveq #-1,d0
bra.s _scsiwaitr
_scsiwaitok:moveq #0,d0
_scsiwaitr: rts
_scsidone: ori.b #$11,(s_icr).w
_scsidonel: btst #5,(s_idstat).w
beq.s _scsidoneok
cmp.l (a0),d1
bhi.s _scsidonel
moveq #-1,d0
bra.s _scsidoner
_scsidoneok:moveq #0,d0
_scsidoner: andi.b #$ef,(s_icr).w
rts
_scsisuba: move.w d0,-(sp)
bsr.s _scsiwait
bmi.s _scsisuba2
move.b 1(sp),(s_data).w
bsr.s _scsidone
_scsisuba2: addq.l #2,sp
rts
_scsisubb: move.b #$80,(s_icr).w
bsr.s _scsidelay250ms
_scsisubbl: cmp.l (a0),d1
bhi.s _scsisubbl
move.b #0,(s_icr).w
bsr.s _scsidelay1s
_scsisubbl2:cmp.l (a0),d1
bhi.s _scsisubbl2
rts
_scsidelay250ms:
movea.w #_hz_200,a0
moveq #51,d1
add.l (a0),d1
rts
_scsidelay1s:
movea.w #_hz_200,a0
move.l #201,d1
add.l (a0),d1
rts
ENDIF
*+
* Wait for status to come back
*-
w4int: move.l #10*200,d0 ; d0 = time-out limit (D_WORST)
add.l (_hz_200).w,d0 ; d0 = expiration time
wi0: btst #5,(gpip).w ; interrupt?
beq.s wi0 ; if so, out of the loop
cmp.l (_hz_200).w,d0 ; time-out?
bhi.s wi0 ; if not, wait some more
moveq #-1,d0 ; else, return time-out
bra.s wi3
wi0: moveq #0,d0 ; clear d0
move.b (ide_comst).l,d0 ; d0.b = status returned
btst #0,d0 ; any error?
bne.s wi2 ; if yes, return error code
btst #3,d0 ; else DRQ?
bne.s wi3 ; if so, just return
moveq #0,d0 ; else return OK
bra.s wi3
wi2: move.b (ide_param).l,d0 ; else d0.b = error bits
wi3: rts ; return status or error code
*+
* ideread() - reads from 1 to 256 sectors as specified in the Task File,
* beginning at the specified sector.
* - sector count equal to 0 requests 256 sectors.
*
* ideread(nhd, nspt, sectnum, count, buf, pdev)
* WORD nhd; 4(sp).w ; # of data heads on pdev
* WORD nspt; 6(sp).w ; # of physical sectors per track
* LONG sectnum; 8(sp).l ; logical block address
* WORD count; $c(sp).w ; # of sectors to read
* BYTE *buf; $e(sp).l ; $f(sp)=high $10(sp)=mid $11(sp)=low
* WORD pdev; $12(sp).w ; physical device number
*-
_ideread: bsr set_dhcs ; set physical address
movea.l $e(sp),a0 ; a0 -> buffer to read into
move.b $d(sp),(ide_seccn).l ; set sector count
move.w $c(sp),d1 ; d1.w = # of sectors to read
subq.w #1,d1 ; dbra likes one less
move.b #0,(ide_stat2).l ; enable interrupt
move.b #$20,(ide_comst).l ; set command code IDEREAD
ider0: bsr.s w4int ; wait for interrupt
tst.w d0 ; successful?
bmi.s ider1 ; if timed-out, return
btst #3,d0 ; DRQ?
beq.s ider1 ; if not, return
bsr readbuf ; fill sector buffer
dbra d1,ider0 ; more to read?
moveq #0,d0 ; everything's fine
ider1: rts
*+
* idewrite() - writes from 1 to 256 sectors as specified in the Task File,
* beginning at the specified sector.
* - sector count equal to 0 requests 256 sectors.
*
* idewrite(nhd, nspt, sectnum, count, buf, pdev)
* WORD nhd; 4(sp).w ; # of data heads on pdev
* WORD nspt; 6(sp).w ; # of physical sectors per track
* LONG sectnum; 8(sp).l ; logical block address
* WORD count; $c(sp).w ; # sectors to read
* BYTE *buf; $e(sp).l ; $f(sp)=high $10(sp)=mid $11(sp)=low
* WORD pdev; $12(sp).w ; physical device number
*-
_idewrite: bsr.s set_dhcs ; set physical address
movea.l $e(sp),a0 ; a0 -> buffer to write from
move.b $d(sp),(ide_seccn).l ; set sector count
move.w $c(sp),d1 ; d1.w = # of sectors to read
subq.w #1,d1 ; dbra likes one less
move.b #0,(ide_stat2).l ; enable interrupt
move.b #$30,(ide_comst).l ; set command code IDEWRITE
idew0: btst #3,(ide_stat2).l ; DRQ?
beq.s idew0 ; if not, wait longer
idew1: bsr wrtbuf ; fill sector buffer
bsr w4int ; wait for interrupt
tst.w d0 ; successful?
bmi.s idew2 ; if timed-out, return
btst #3,d0 ; DRQ?
beq.s idew2 ; if not, return
dbra d1,idew1 ; else go transfer data
moveq #0,d0 ; everything's fine
idew2: rts
*+
* set_dhcs() - convert a logical block address into a physical address.
* - set drive #, head #, cylinder # and sector # in task file.
*
* Passed:
* 8(sp).w = nhd = # of data heads
* $a(sp).w = nspt = # of physical sectors per track
* $c(sp).l = logical block address
* $16(sp).w = physical unit #
*-
set_dhcs: move.l $c(sp),d1 ; d1.l = logical block address
move.w 8(sp),d2 ; d2.w = # of data heads
move.w $a(sp),d0 ; d0.w = # of physical sectors per track
mulu.w d0,d2 ; d2.l = # of sectors per cylinder = # heads * # of sectors per track
divu.w d2,d1 ; d1.w = cylinder # = log block addr / #spc
move.b d1,(ide_zylow).l ; set cylinder low
lsr.l #8,d1 ; d1.b = cylinder high
move.b d1,(ide_zyhig).l ; set cylinder high
lsr.l #8,d1 ; d1.l = sector # within the cyl
divu.w d0,d1 ; d1.w = head # = sector # within cyl / #spt
move.w $16(sp),d0 ; d0.w = physical unit #
andi.b #7,d0 ; mask off flags from physical unit #
lsl.b #4,d0 ; shift unit # to place
or.b d0,d1 ; or in drive #
move.b d1,(ide_headn).l ; set drive and head #
swap d1 ; d1.w = sector # (base 0)
addq.w #1,d1 ; = sector # + 1 (base 1)
move.b d1,(ide_stars).l ; set sector #
rts
*+
* identify() - allows the Host to receive parameter information from
* the drive.
*
* identify(pdev, buf)
* WORD pdev; 4(sp).w ; physical unit #
* BYTE *buf; 6(sp).l ; buffer to put data
*-
IF ROM_TOS306
_initdevpar:move.w 4(sp),d0 ; d0 = physical unit #
andi.b #7,d0 ; mask off flags (if any)
lsl.w #4,d0 ; shift unit # to place
move.b d0,(ide_headn).l ; set drive #
move.w 6(sp),d0
subq.b #1,d0
or.b d0,(ide_headn).l ; # of heads - 1
move.b 9(sp),(ide_seccn).l ; # of sectors per track
move.b #0,(ide_stat2).l ; enable interrupt
move.b #$91,(ide_comst).l ; set command code INITIALIZE DEVICE PARAMETERS
bra w4int
ENDIF
*+
* identify() - allows the Host to receive parameter information from
* the drive.
*
* identify(pdev, buf)
* WORD pdev; 4(sp).w ; physical unit #
* BYTE *buf; 6(sp).l ; buffer to put data
*-
_identify: move.w 4(sp),d0 ; d0 = physical unit #
andi.b #7,d0 ; mask off flags (if any)
lsl.b #4,d0 ; shift unit # to place
move.b d0,(ide_headn).l ; set drive #
movea.l 6(sp),a0 ; a0 -> buffer
move.b #0,(ide_stat2).l ; enable interrupt
move.b #$ec,(ide_comst).l ; set command code IDENTIFY
bsr w4int ; wait for interrupt
tst.w d0 ; successful?
bmi.s id0 ; if timed-out, return
btst #3,d0 ; DRQ?
beq.s id0 ; if not, return with error
bsr.s readbuf ; read data
moveq #0,d0 ; everything's fine
id0: rts
*+
* readbuf() - reads 512 bytes (128 longs) of data into sector buffer.
*
* Passed:
* a0.l = buffer to store data read from sector buffer
*-
readbuf:
IF ROM_TOS306
moveq #$7f,d0 ; d0 = (# of words of data to read) - 1
lea (ide_datar).l,a1 ; a1 -> data bus
rb0: move.l (a1),(a0)+ ; read data from bus
dbra d0,rb0 ; repeat until all done
ELSE
moveq #$1f,d0 ; d0 = (# of words of data to read / 8) - 1
lea (ide_datar).l,a1 ; a1 -> data bus
rb0: move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
move.w (a1),(a0)+ ; read data from bus
dbra d0,rb0 ; repeat until all done
ENDIF
rts
*+
* wrtbuf() - writes 512 bytes (128 longs) of data to sector buffer.
*
* Passed:
* a0.l = buffer with data to write to sector buffer
*-
wrtbuf:
IF ROM_TOS306
moveq #$7f,d0 ; d0 = (# of words of data to read) - 1
lea (ide_datar).l,a1 ; a1 -> data bus
wb0: move.l (a0)+,(a1) ; write data to bus
dbra d0,wb0 ; repeat until all done
ELSE
moveq #$1f,d0 ; d0 = (# of words of data to read / 8) - 1
lea (ide_datar).l,a1 ; a1 -> data bus
wb0: move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
move.w (a0)+,(a1) ; write data to bus
dbra d0,wb0 ; repeat until all done
ENDIF
rts
*+
* _iderdy() - test if the IDE drive is ready
*
* Passed:
* d0.b = IDE drive unit #
*
* Returns: 0 - if drive is NOT ready
* 1 - if drive is ready
*-
_iderdy: andi.b #7,d0 ; mask off flags (if any)
lsl.b #4,d0 ; shift unit # to place
move.b d0,(ide_headn).l ; set drive #
IF ROM_TOS306
move.l #5*200,d0 ; set up timer IDERDY
add.l (_hz_200).w,d0
ir0: btst #6,(ide_stat2).l
bne.s ir1
ELSE
move.b #$50,d1 ; ready status
move.l #5*200,d0 ; set up timer IDERDY
add.l (_hz_200).w,d0
ir0: cmp.b (ide_stat2).l,d1 ; is drive ready and not busy?
beq.s ir1 ; if so, return with drive ready
ENDIF
cmp.l (_hz_200).w,d0 ; time-out yet?
bcc.s ir0 ; if not, wait longer
moveq #0,d0 ; else return drive NOT ready
rts
ir1: moveq #1,d0 ; else, drive is ready
rts
*+
* gcparm() - get current drive parameters
*
* gcparm(buf)
* char *buf; $4(sp).l /* -> data returned by identify() */
*
* Returns:
* d0.w = # of default cylinders
* d1.w = # of default heads
* d2.w = # of default sectors per track
*-
IF !ROM_TOS306
_gcparm: movea.l 4(sp),a0 ; a0 -> data buffer
adda.w #$50,a0 ; a0 -> where Conner model number is (CONMDL)
move.l a0,-(sp)
pea (cp2024).l
move.w #6,-(sp)
bsr.s strcmp ; compare model# with "CP2024"
adda.w #$a,sp ; clean up stack
tst.w d0 ; is unit the CP2024 (Kato 20Mb)?
bne.s gcp0 ; if not, handle the normal way else return default values of CP2024
move.w #$267,d0 ; d0.w = # of cylinders (CP20NCYL)
move.w #4,d1 ; d1.w = # of heads (CP20NHEAD)
move.w #$11,d2 ; d2.w = # of spt (CP20NSPT)
bra.s gcpend
gcp0: movea.l 4(sp),a0
move.w 2(a0),d0 ; d0.w = # of cylinders
move.w 6(a0),d1 ; d1.w = # of heads
move.w $c(a0),d2 ; d2.w = # of sectors per track
gcpend: rts
conner: DC.B 'Conner',0
DC.B $00
cp2024: DC.B 'CP2024',0
DC.B $00
*+
* strcmp() - compare two strings
*
* Passed:
* 4(sp).w = n (# of bytes to compare)
* 6(sp).l = address of first string
* 10(sp).l = address of second string
*
* Returns:
* d0.w = 0 if first n bytes of the 2 strings are the same
* = non-0 otherwise
*-
strcmp: movem.l d1/a0-a1,-(sp) ; save registers d1, a0 and a1
move.w $10(sp),d1 ; d1 = byte count
subq.w #1,d1 ; dbra likes one less
movea.l $12(sp),a0 ; a0 -> string 1
movea.l $16(sp),a1 ; a1 -> string 2
moveq #1,d0 ; assume strings are not the same
str0: cmpm.b (a0)+,(a1)+ ; characters the same?
bne.s str1 ; if not, return
dbra d1,str0 ; else compare next character
moveq #0,d0 ; the strings are the same
str1: movem.l (sp)+,d1/a0-a1 ; restore registers d1, a0 and a1
rts
ENDIF
**************************************************************************
* *
**************************************************************************
IF ROM_TOS306
readCurrentTime:
bsr checkRTC
bcs.s readCurrentTime2
move.b #$d,(rtcadd).w
move.b (rtcdat).w,d0
btst #7,d0
bne.s readCurrentTime2
move.l #$12c80000,-(sp) ; default date: July 4th, 1989, 12:00am
bsr writeRTCTime
addq.w #4,sp
readCurrentTime:
bsr.s readRTCTime
cmp.l #-1,d0
beq.s readCurrentTime2
moveq #0,d0
readCurrentTime2:
rts
readRTCTime:bsr checkRTC
bcs.s readCurrentTime2
move.b #$d,(rtcadd).w
btst #7,(rtcdat).w
beq readRTCTimeErr
move sr,d2
move.w d2,d0
or.w #$700,d0
readRTCTime3:move.b #$a,(rtcadd).w
btst #7,(rtcdat).w
bne.s readRTCTime3
moveq #0,d0
move.l d0,d1
move.b #0,(rtcadd).w
move.b (rtcdat).w,d0
asr.w #1,d0
move.b #2,(rtcadd).w
move.b (rtcdat).w,d1
bfins d1,d0{21:6}
move.b #4,(rtcadd).w
move.b (rtcdat).w,d1
bfins d1,d0{16:5}
move.b #7,(rtcadd).w
move.b (rtcdat).w,d1
bfins d1,d0{11:5}
move.b #8,(rtcadd).w
move.b (rtcdat).w,d1
bfins d1,d0{7:4}
move.b #9,(rtcadd).w
move.b (rtcdat).w,d1
sub.b #12,d1
bfins d1,d0{0:7}
move d2,sr
move sr,d2
ori #$700,sr
move.w d0,(systemDate).l
swap d0
move.w d0,(systemTime).l
swap d0
move d2,sr
rts
readRTCTimeErr:
moveq #-1,d0
rts
writeRTCTime:bsr checkRTC
bcs readCurrentTime2
move.l 4(sp),d0
move.b #$b,(rtcadd).w
move.b #$80,(rtcdat).w
move.b #$a,(rtcadd).w
move.b #$2a,(rtcdat).w
move.b #$b,(rtcadd).w
move.b #$8e,(rtcdat).w
move.b #0,(rtcadd).w
bfextu d0{27:5},d1
add.b d1,d1
move.b d1,(rtcdat).w
move.b #2,(rtcadd).w
bfextu d0{21:6},d1
move.b d1,(rtcdat).w
move.b #4,(rtcadd).w
bfextu d0{16:5},d1
move.b d1,(rtcdat).w
move.b #7,(rtcadd).w
bfextu d0{11:5},d1
move.b d1,(rtcdat).w
move.b #8,(rtcadd).w
bfextu d0{7:4},d1
move.b d1,(rtcdat).w
move.b #9,(rtcadd).w
bfextu d0{0:7},d1
add.b #$c,d1
move.b d1,(rtcdat).w
move.b #$b,(rtcadd).w
move.b #$e,(rtcdat).w
rts
checkRTC: movea.l sp,a0
movea.l (busexception).w,a1
move.l #checkRTC2,(busexception).w
move.b #0,(rtcadd).w
move.b (rtcdat).w,d0
move.l a1,(busexception).w
andi #$fe,ccr
rts
checkRTC2: movea.l a0,sp
move.l a1,(busexception).w
ori #1,ccr
rts
_NVMaccess: moveq #-5,d0
move.w 4(sp),d1 ; op = 0: read NVRAM
beq.s _NVMread
cmp.w #2,d1 ; op = 2: initialize NVRAM and update checksum
beq.s _NVMinit
bhi.s _NVMaccessr ; (unknown op)
* write into NVRAM and update the checksum
bsr.s _NVMvalid ; op = 1: write NVRAM
tst.w d0
bne.s _NVMaccessr
movea.l $a(sp),a0 ; buffer
bra.s _NVMwrt2
_NVMwrtl: move.b d1,(a1) ; select register
move.b (a0)+,(a2) ; write value
addq.w #1,d1 ; next reg
_NVMwrt2: dbra d2,_NVMwrtl
bsr _NVMchksum ; calculate the checksum
move.b #$3f,(a1)
move.b d0,(a2) ; checksum into reg 63
not.b d0
move.b #$3e,(a1)
move.b d0,(a2) ; inverted checksum into reg 62
moveq #0,d0 ; (ok)
_NVMaccessr:rts
* read from NVRAM and also validate the checksum
_NVMread: bsr.s _NVMvalid
cmp.w #-5,d0 ; argument error?
beq.s _NVMaccessr ; (fail)
movea.l $a(sp),a0 ; buffer
bra.s _NVMread2
_NVMreadl: move.b d1,(a1) ; select register
move.b (a2),(a0)+ ; read value
addq.w #1,d1 ; next reg
_NVMread2: dbra d2,_NVMreadl
rts
* erase NVRAM and update the checksum
_NVMinit: lea (rtcadd).w,a1
lea (rtcdat).w,a2
moveq #0,d0 ; fill value = 0
moveq #14,d1
moveq #50-1,d2 ; fill 48 bytes plus checksum
_NVMinitl: move.b d1,(a1) ; select register
move.b d0,(a2) ; clear
addq.w #1,d1 ; next reg
dbra d2,_NVMinitl
move.b #$3e,(a1) ; inverted checksum of 0 is 0xFF
move.b #$ff,(a2)
rts
* validate checksum in NVRAM and function parameters
_NVMvalid: bsr.s _NVMchksum ; calculate the checksum
move.b d0,d1
moveq #-12,d0 ; default: checksum error
move.b #$3f,(a1) ; register 63
cmp.b (a2),d1 ; contains the checksum
bne.s _NVMvalidr ; (error)
not.b d1
move.b #$3e,(a1) ; register 62
cmp.b (a2),d1 ; contains the inverted checksum
bne.s _NVMvalidr ; (error)
moveq #-5,d0 ; default: argument error
move.w $a(sp),d1 ; count
cmp.w #48,d1 ; more than 48 bytes?
bcc.s _NVMvalidr ; (error)
move.w $c(sp),d2 ; start
bmi.s _NVMvalidr ; negative => error
add.w d1,d2 ; start + count
cmp.w #48,d2 ; >48
bhi.s _NVMvalidr ; (error)
moveq #0,d0 ; no error
_NVMvalidr: move.w $c(sp),d2 ; d2: count
move.w $a(sp),d1
add.w #14,d1 ; d1: first register
rts
_NVMchksum: lea (rtcadd).w,a1
lea (rtcdat).w,a2
moveq #0,d0 ; checksum = 0
moveq #14,d1 ; NVRAM starts at register 14
moveq #48-1,d2 ; 48 bytes
_NVMcheckl: move.b d1,(a1) ; select register
add.b (a2),d0 ; sum values to checksum
addq.w #1,d1
dbra d2,_NVMcheckl
rts
ELSE
readCurrentTime:bsr.s checkRTC
bcs.s readCurrentTime2
bsr.s readRTCTime
cmp.l #-1,d0
beq.s readCurrentTime2
moveq #0,d0
readCurrentTime2:rts
IF ROM_STBOOK
checkRTC:
movea.w #rtc_sec,a0
move.b #$00,$1d(a0) ; Clear RTC Test register
move.b #$0c,$1f(a0) ; Reset 1 Hz & 16 Hz alarm pulse
move.b #$08,$1b(a0) ; Clock Start, Alarm off, Bank 0
moveq #0,d0 ; The RTC is always available
rts
ELSE
checkRTC: movea.w #rtc_sec,a0
move.l (busexception).w,d2
movea.l sp,a2
move.l #checkRTC2,(busexception).w
bset #0,$1b(a0)
move.l d2,(busexception).w
move.w #$a05,d0
movep.w d0,5(a0)
movep.w 5(a0),d1
and.w #$f0f,d1
cmp.w d0,d1
bne.s checkRTC3
move.b #1,1(a0)
bclr #0,$1b(a0)
move.b #0,$1d(a0)
rts
checkRTC2: movea.l a2,sp
move.l d2,(busexception).w
checkRTC3: ori #1,ccr ; RTC not available
rts
ENDIF
readRTCTime:bsr.s checkRTC
bcs readRTCTimeErr
lea (rtcbufa).w,a1
lea (rtcbufb).w,a2
bsr readRTCTimeMask
readRTCTime2:exg a1,a2
bsr readRTCTimeMask
moveq #$c,d0
readRTCTime3:move.b (a1,d0.w),d1
cmp.b (a2,d0.w),d1
bne.s readRTCTime2
dbra d0,readRTCTime3
moveq #0,d0
move.b $b(a1),d0
mulu.w #10,d0
add.b $c(a1),d0
asr.w #1,d0
move.w d0,d1
moveq #0,d0
move.b 9(a1),d0
mulu.w #10,d0
add.b $a(a1),d0
asl.w #5,d0
add.w d0,d1
moveq #0,d0
move.b 7(a1),d0
mulu.w #10,d0
add.b 8(a1),d0
asl.w #8,d0
asl.w #3,d0
add.w d0,d1
swap d1
moveq #0,d0
move.b 4(a1),d0
mulu.w #10,d0
add.b 5(a1),d0
move.w d0,d1
moveq #0,d0
move.b 2(a1),d0
mulu.w #10,d0
add.b 3(a1),d0
asl.w #5,d0
add.w d0,d1
moveq #0,d0
move.b (a1),d0
mulu.w #10,d0
add.b 1(a1),d0
asl.w #8,d0
asl.w #1,d0
add.w d0,d1
move sr,d2
ori #$700,sr
move.w d1,(systemDate).l
swap d1
move.w d1,(systemTime).l
move d2,sr
move.l d1,d0
rts
readRTCTimeMask:moveq #$c,d0
moveq #1,d1
readRTCTimeMask2:move.b (a0,d1.w),d2
and.b #$f,d2
move.b d2,(a1,d0.w)
addq.w #2,d1
dbra d0,readRTCTimeMask2
rts
readRTCTimeErr:moveq #-1,d0
rts
writeRTCTime:bsr checkRTC
bcs no_RTC_found
lea (rtcbufa).w,a1
movea.w 4(sp),a2
bsr calcRTCWeekday
move.b d0,6(a1)
move.w 6(sp),d1
move.w d1,d0
and.l #$1f,d0
add.w d0,d0
divu.w #10,d0
move.b d0,$b(a1)
swap d0
move.b d0,$c(a1)
move.w d1,d0
lsr.w #5,d0
and.l #$3f,d0
divu.w #10,d0
move.b d0,9(a1)
swap d0
move.b d0,$a(a1)
lsr.w #8,d1
lsr.w #3,d1
ext.l d1
divu.w #10,d1
move.b d1,7(a1)
swap d1
move.b d1,8(a1)
move.w 4(sp),d1
move.w d1,d0
and.l #$1f,d0
divu.w #10,d0
move.b d0,4(a1)
swap d0
move.b d0,5(a1)
move.w d1,d0
lsr.w #5,d0
and.l #$f,d0
divu.w #10,d0
move.b d0,2(a1)
swap d0
move.b d0,3(a1)
lsr.w #1,d1
lsr.w #8,d1
ext.l d1
move.l d1,d2
divu.w #10,d1
move.b d1,(a1)
swap d1
move.b d1,1(a1)
divu.w #4,d2
swap d2
move.b #$e,$1f(a0)
bset #0,$1b(a0)
move.b #1,$15(a0)
move.b d2,$17(a0)
bclr #0,$1b(a0)
bclr #3,$1b(a0)
moveq #$c,d0
moveq #1,d1
writeRTCTime2:move.b (a1,d0.w),(a0,d1.w)
addq.w #2,d1
dbra d0,writeRTCTime2
bset #3,$1b(a0)
moveq #0,d0
rts
no_RTC_found:moveq #-1,d0
rts
calcRTCWeekday:moveq #2,d2
move.w a2,d0
lsr.w #8,d0
lsr.w #1,d0
add.w d0,d2
move.w d0,d1
lsr.w #2,d1
add.w d1,d2
move.w a2,d1
lsr.w #5,d1
and.w #$f,d1
and.w #3,d0
bne.s calcRTCWeekday2
cmp.w #2,d1
bhi.s calcRTCWeekday2
subq.w #1,d2
calcRTCWeekday2:subq.w #1,d1
add.w d1,d1
add.w daysOffsetToMonths(pc,d1.w),d2
move.w a2,d1
and.w #$1f,d1
add.w d1,d2
divu.w #7,d2
swap d2
moveq #0,d0
move.w d2,d0
rts
daysOffsetToMonths:DC.W $0000,$001f,$003b,$005a
DC.W $0078,$0097,$00b5,$00d4
DC.W $00f3,$0111,$0130,$014e
_Waketime: move.l 4(sp),d0
beq clearRTCWaketime
cmp.l #-1,d0
beq readRTCWaketime
cmp.l #1,d0
beq enableRTCWaketime
bsr readRTCTime
cmp.l #-1,d0
beq enableRTCWaketimeDone
move.l 4(sp),d1
and.b #$e0,d0
and.b #$e0,d1
cmp.l d1,d0 ; is the new wakeup time already in the past?
bcc.s _Waketime_ret1
move.l (waketimeAlarm).l,d2
beq.s _Waketime2
cmp.l d2,d0
bhi.s _Waketime2
cmp.l d2,d1
bcc.s _Waketime_ret2
_Waketime2: movea.l d0,a0
move.l d1,d2
and.l #$1fffff,d2
and.l #$1fffff,d0
cmp.l d2,d0 ; new wakeup time is _now_?
beq.s _Waketime3
move.l d1,(waketimeAlarm).l
moveq #0,d0
rts
_Waketime3: and.l #$1f0000,d2
cmp.l #$10000,d2
beq.s _Waketime_ret5
and.l #$ffe0ffff,d1
or.l #$10000,d1
move.l d1,(waketimeAlarm).l
moveq #4,d0
rts
_Waketime_ret5:move.l a0,d0
add.l #$10000,d0
move.l d0,(waketimeAlarm).l
moveq #5,d0
rts
_Waketime_ret1:moveq #1,d0
rts
_Waketime_ret2:moveq #2,d0
rts
readRTCWaketime:move.l (waketimeAlarm).l,d0
rts
clearRTCWaketime:bsr checkRTC
bcs no_RTC_found
bclr #2,$1b(a0) ; RTC Alarm off
move.b #$d,$1f(a0) ; Reset 1Hz, 16 Hz and Alarm
clr.l (waketimeAlarm).l
moveq #3,d0
rts
enableRTCWaketime:bsr readRTCTime
cmp.l #-1,d0
beq enableRTCWaketimeDone
move.l (waketimeAlarm).l,d1
beq enableRTCWaketimeDone
and.b #$e0,d1
move.l d1,d2
and.b #$e0,d0
cmp.l d1,d0 ; is the new wakeup time already in the past?
bhi.s enableRTCWaketimeDone
bne.s enableRTCWaketime2
and.b #$1f,d2
cmp.b #7,d2
bcs.s enableRTCWaketime3
enableRTCWaketime2:movea.w #rtc_sec,a0
bset #0,$1b(a0) ; RTC select Bank 1
move.l (waketimeAlarm).l,d1
move.w d1,d0
lsr.w #5,d0 ; Minutes
and.l #$3f,d0
divu.w #10,d0
move.b d0,7(a0) ; RTC 1-minute alarm register
swap d0
move.b d0,5(a0) ; RTC 10-minute alarm register
move.w d1,d0
lsr.w #8,d0
lsr.w #3,d0 ; Hours
ext.l d0
divu.w #10,d0
move.b d0,$b(a0) ; RTC 1-hour alarm register
swap d0
move.b d0,9(a0) ; RTC 10-hour alarm register
move.l d1,d0
swap d0
and.w #$1f,d0 ; Days
ext.l d0
divu.w #10,d0
move.b d0,$11(a0) ; RTC 1-hour alarm register
swap d0
move.b d0,$f(a0) ; RTC 10-day alarm register
movea.w (waketimeAlarm).l,a2
bsr calcRTCWeekday
move.b d0,$d(a0) ; RTC day-of-the-week alarm register
bset #2,$1b(a0) ; RTC Alarm on
bclr #0,$1b(a0) ; RTC select Bank 0
enableRTCWaketimeDone:moveq #0,d0
rts
enableRTCWaketime3:clr.l (waketimeAlarm).l
moveq #-1,d0
rts
ENDIF
**************************************************************************
* *
* cp/m-68k atari rbp bios *
* basic input/output subsystem *
* copyright 1984, atari corporation *
* all rights reserved. *
* atari confidential *
* *
**************************************************************************
**************************************************************************
* *
* convert ikbd real-time clock format to jdos format *
* *
**************************************************************************
jdostime: lea (clkrec).w,a0
bsr bcdbin
subi.b #80,d0 ; adjust so that 1980 => 0 for time base
move.b d0,d2
asl.l #4,d2
bsr bcdbin
add.b d0,d2
asl.l #5,d2
bsr bcdbin
add.b d0,d2
asl.l #5,d2
bsr bcdbin
add.b d0,d2
asl.l #6,d2
bsr bcdbin
add.b d0,d2
asl.l #5,d2
bsr bcdbin
lsr.b #1,d0 ; adjust to provide two second increments...
add.b d0,d2 ; ...another @!#%@#$% kludge, thank you !
move.l d2,(datetime).w
move.b #0,(newtod).w ; clear handshaking flag
rts
readIKBDTime:move.b #$ff,(newtod).w
move.b #$1c,d1
bsr ikbdput
movea.l (_hz_200).w,a0
adda.w #200,a0
moveq #0,d0
readIKBDTime2:cmpa.l (_hz_200).w,a0
bcs.s readIKBDTimex
tst.b (newtod).w
bne.s readIKBDTime2
move.l (datetime).w,d0
readIKBDTimex:rts
writeIKBDTime:move.l 4(sp),(newtime).w
lea (kmbuf).l,a0
move.l (newtime).w,d2
move.b d2,d0
andi.b #$1f,d0
asl.b #1,d0
bsr.s binbcd
lsr.l #5,d2
move.b d2,d0
andi.b #$3f,d0
bsr.s binbcd
lsr.l #6,d2
move.b d2,d0
andi.b #$1f,d0
bsr.s binbcd
lsr.l #5,d2
move.b d2,d0
andi.b #$1f,d0
bsr.s binbcd
lsr.l #5,d2
move.b d2,d0
andi.b #$f,d0
bsr.s binbcd
lsr.l #4,d2
move.b d2,d0
andi.b #$7f,d0
bsr.s binbcd
addi.b #$80,(a0)
move.b #$1b,d1
bsr ikbdput
moveq #5,d3
lea (oclkrec).l,a2
bsr ikbdstr
move.b #$1c,d1
bsr ikbdput
rts
**************************************************************************
* *
* convert a byte from binary to bcd format *
* *
* entry: d0.l - value *
* *
**************************************************************************
binbcd: moveq #0,d1
move.b d0,d1
divs.w #10,d1
asl.w #4,d1
move.w d1,d0
swap d1
add.w d1,d0
move.b d0,-(a0) ; transfer to output clock buffer
rts
**************************************************************************
* *
* convert a byte from bcd format to binary *
* *
* entry: a0.l - pointer to byte *
* *
**************************************************************************
bcdbin: move.b (a0)+,d0 ; get bcd byte
move.b d0,d1
and.w #$0f,d0 ; dump high nibble
and.w #$f0,d1 ; isolate high nibble
asr.w #4,d1 ; dump low nibble
mulu.w #10,d1 ; high nibble * 10
add.w d1,d0 ; + low nibble
rts
**************************************************************************
* *
* midi output status *
* *
* entry: *
* *
* word midiost() *
* *
* returns true/okay to send = -1, false/not ready = 0 *
* *
**************************************************************************
midiost: moveq #-1,d0 ; pre-set to true
move.b (midictl).w,d2 ; grab midi status
btst #1,d2
bne.s midiox ; status okay to send
moveq #0,d0 ; status not okay
midiox: rts
**************************************************************************
* *
* write char to midi port *
* *
* entry: *
* *
* void midiwc(chr) *
* word chr *
* *
**************************************************************************
midiwc: move.w 6(sp),d1
midiput: lea (midictl).w,a1 ; point to midi register base
midput1: move.b (a1),d2 ; grab midi status
btst #1,d2
beq.s midput1
move.b d1,2(a1)
rts ; done for now
**************************************************************************
* *
* put string to midi routine *
* *
* entry: *
* *
* void midiws(size,ptr) *
* word size *
* long ptr *
* *
**************************************************************************
midiws: moveq #0,d3
move.w 4(sp),d3 ; get size of string buffer - 1
movea.l 6(sp),a2 ; get string address
midp1: move.b (a2)+,d1
bsr.s midiput
dbra d3,midp1
rts
**************************************************************************
* *
* get midi receiver buffer status *
* *
* entry: *
* *
* word midistat() *
* *
* -1 signifies true/okay 0 - signifies false/no characters *
* *
**************************************************************************
midstat: lea (mbufrec).w,a0 ; point to midi i/o bufrec
lea (midictl).w,a1 ; point to midi register base
moveq #-1,d0 ; set result to true
lea 6(a0),a2
lea 8(a0),a3
cmpm.w (a3)+,(a2)+ ; atomic buffer empty test
bne.s midist1 ; branch of not, assume d0 is "clr.w"'ed
moveq #0,d0 ; set result to false
midist1: rts
**************************************************************************
* *
* getchar routine for midi port *
* *
* this routine transfers characters from a input queue that is *
* filled by an automatic interrupt routine. the interrupt *
* routine handles the actual transfer of the character from the *
* i/o port. *
* *
* entry: *
* *
* long midiin() *
* *
* long data returned represents upper three bytes of time stramp *
* and least significant byte as data *
* *
**************************************************************************
* assume that a0/a1 are inited by the midstat call for the rest of
* this routine.
midin: bsr.s midstat ; see if key pressed
tst.w d0
beq.s midin ; wait until byte comes in
move sr,-(sp) ; protect this upcoming test
ori #$700,sr
move.w 6(a0),d1 ; get current head pointer offset from buffer
cmp.w 8(a0),d1 ; head=tail?
beq.s mwi2 ; yes
* check for wrap of pointer
addq.w #1,d1 ; i=j+1
cmp.w 4(a0),d1 ; ? i>= current bufsiz?
bcs.s mwi1 ; no...
moveq #0,d1 ; wrap pointer
mwi1: movea.l (a0),a1 ; get base address of buffer
and.l #$ffff,d1
IF !ROM_TOS306
moveq #0,d0
ENDIF
move.b (a1,d1.l),d0 ; get character
move.w d1,6(a0) ; store new head pointer to buffer record
mwi2: move (sp)+,sr
rts
**************************************************************************
* *
* parallel i/o port service routine *
* *
* this set of routines is for general parallel i/o *
* *
* entry to listout *
* *
* entry to listin *
* *
* exit from listin *
* *
**************************************************************************
_lstout: btst #4,(pconfig).w
bne _auxout
move.l (_hz_200).w,d2 ; d2 = hz_200 - prt_to
sub.l (prt_to).w,d2 ; (compute time since last timeout)
cmpi.l #5*200,d2 ; do "fake" timeout if we timed out within
bcs.s lperr ; the last five seconds
move.l (_hz_200).w,d2 ; d2 = starting time for this char
pt0: bsr _lstostat ; go get parallel port status
tst.w d0 ; ...and check for high (busy)
bne.s pt1 ; port is ready -- print the char
move.l (_hz_200).w,d3 ; d3 = hz_200 - d2
sub.l d2,d3
cmpi.l #$1770,d3 ; check for 30 second delta
blt.s pt0 ; continue of no timeout
lperr: moveq #0,d0 ; return value of 0 indicates timeout
move.l (_hz_200).w,(prt_to).w ; record time of last timeout
rts
pt1: move sr,d3 ; save status register
ori #$700,sr ; protect upcoming switching of the port setting
moveq #7,d1 ; get current io enable register contents
bsr gientry
ori.b #$80,d0 ; set port b for output
move.b #$87,d1 ; set to write to io enable
bsr gientry
move d3,sr ; restore status register
move.w 6(sp),d0 ; retrieve byte to be sent and...
move.b #$8f,d1 ; write out byte to parallel port
bsr gientry
move sr,-(sp)
ori #$700,sr
bsr.s strobeon
bsr.s strobeon
bsr.s strobeoff
move (sp)+,sr
moveq #-1,d0 ; set d0=-1 for good transfer status
rts
strobeoff: moveq #$20,d2 ; set strobe off
bra onbit ; go set it!!
strobeon: move.b #$df,d2 ; set strobe on
bra offbit ; set strobe now...
_lstin: moveq #7,d1 ; get current io enable register contents
bsr gientry
andi.b #$7f,d0 ; set port b for input
move.b #$87,d1 ; set to write to io enable
bsr gientry
bsr.s strobeoff ; busy off!
lstibusy: bsr.s _lstostat ; go get parallel port status
tst.w d0 ; ...and check for high (busy)
bne.s lstibusy ; loop till high...
bsr.s strobeon
moveq #$f,d1 ; init to use gientry routine to read
bra gientry ; now get the byte from the parallel port
* d0.l contains the byte of data from the port
* the 'bra' is implied rts from this routine
**************************************************************************
* *
* parallel port status routine *
* *
**************************************************************************
_lstostat: lea (gpip).w,a0 ; point to mfp register base
moveq #-1,d0 ; pre-init to true (parallel port ready)
btst #0,(a0)
beq.s lst1
moveq #0,d0 ; parallel port busy
lst1: rts
**************************************************************************
* *
* auxillary port input status routine *
* *
**************************************************************************
auxistat: lea (rbufrec).w,a0 ; point to rs-232 buffer record
_auxistat: moveq #-1,d0 ; set result to true
lea 6(a0),a1 ; head
lea 8(a0),a0 ; tail
cmpm.w (a0)+,(a1)+ ; atomic buffer empty test
bne.s auxist1
moveq #0,d0 ; set result to false
auxist1: rts
**************************************************************************
* *
* auxillary input routine *
* *
**************************************************************************
auxin: lea (rbufrec).w,a0 ; point to rs-232 buffer record
lea (gpip).w,a2
_auxin: bsr rs232get
move.w d0,-(sp)
tst.b $20(a0) ; flow control active?
beq.s auxinrts ; (no)
move.w 8(a0),d0 ; tail
sub.w 6(a0),d0 ; - head
bpl.s auxin2 ; underflow?
add.w 4(a0),d0 ; + size
auxin2: cmp.w $a(a0),d0
bgt.s auxinrts
tst.b $1e(a0) ; high-water flag already set?
beq.s auxinrts ; no...exit...
bsr.s auxinclrhw
auxinrts: move.w (sp)+,d0
rts
auxinclrhw: clr.b $1e(a0) ; clear high-water flag
btst #0,$20(a0) ; is the rs232 mode xon/xoff?
bne.s auxinclrhw2 ; (yes)
bra rtson
auxinclrhw2:move.b #$11,$21(a0) ; "xon"
bra.s _auxoutwr
**************************************************************************
* *
* auxillary port output status routine *
* *
**************************************************************************
_auxostat: lea (rbufrec+14).w,a0 ; point to rs-232 buffer record
__auxostat: move.w 8(a0),d1 ; tail
bsr wrapin
moveq #-1,d0 ; set result to true
cmp.w 6(a0),d1 ; head
bne.s _auxostatrts
moveq #0,d0 ; set result to false
_auxostatrts:rts
**************************************************************************
* *
* auxillary output routine *
* *
**************************************************************************
_auxout: move.w 6(sp),d0 ; get data
lea (rbufrec+14).w,a0
bsr rs232put ; exit via rs-232 output routine
lea (rbufrec).w,a0
lea (gpip).w,a2
_auxoutwr: tst.b $2c(a2) ; transmitter status
bpl.s _auxoutrts ; not ready
move sr,-(sp)
ori #$700,sr
bsr wr_rs232
move (sp)+,sr
_auxoutrts: rts
**************************************************************************
* *
* ikbd output status *
* *
* entry: *
* *
* word ikbdost() *
* *
* returns true/okay to send = 1, false/not ready = 0 *
* *
**************************************************************************
ikbdost: moveq #-1,d0 ; pre-set to true
move.b (keyctl).w,d2 ; grab ikbd status
btst #1,d2
bne.s ikbdox ; status okay to send
moveq #0,d0 ; status not okay
ikbdox: rts
**************************************************************************
* *
* write char to ikbd port *
* *
* entry: *
* *
* void ikbdwc(chr) *
* word chr *
* *
**************************************************************************
ikbdwc: move.w 6(sp),d1
ikbdput: lea (keyctl).w,a1 ; point to ikbd register base
ikput1: move.b (a1),d2 ; grab keyboard status
btst #1,d2
beq.s ikput1
IF ROM_TOS306
move.w #$400,d0
bsr mfpdelay
ELSE
lea (tcdr).w,a0
move.w #$bf,d0
ikput2: move.b (a0),d2
ikput3: cmp.b (a0),d2
beq.s ikput3
dbra d0,ikput2
ENDIF
move.b d1,2(a1) ; write char to the ikbd port
rts ; done for now
**************************************************************************
* *
* put string to to ikbd routine *
* *
* entry: *
* *
* void ikbdws(size,ptr) *
* word size *
* long ptr *
* *
**************************************************************************
ikbdws:
IF ROM_TOS306
moveq #0,d3
ENDIF
move.w 4(sp),d3
movea.l 6(sp),a2
ikbdstr: move.b (a2)+,d1
bsr.s ikbdput
dbra d3,ikbdstr
rts
constat: lea (kbufrec).w,a0 ; point to ikbd buffer record
moveq #-1,d0 ; set result to true
lea 6(a0),a2 ; head
lea 8(a0),a3 ; tail
cmpm.w (a3)+,(a2)+ ; atomic buffer empty test
bne.s const1 ; branch of not, assume d0 is "clr.w"'ed
moveq #0,d0 ; set result to false
const1: rts
conin: bsr.s constat ; see if key pressed
tst.w d0
beq.s conin ; wait until key pressed
move sr,-(sp) ; protect this upcoming test
ori #$700,sr
move.w 6(a0),d1 ; get current head pointer offset
cmp.w 8(a0),d1 ; head=tail?
beq.s cwi2 ; yes
* check for wrap of pointer
addq.w #4,d1 ; i=h+4
cmp.w 4(a0),d1
bcs.s cwi1 ; no...
moveq #0,d1 ; wrap pointer
cwi1: movea.l (a0),a1 ; get base address of buffer
and.l #$ffff,d1
move.l (a1,d1.l),d0 ; get character
move.w d1,6(a0) ; store new head pointer to buffer
cwi2: move (sp)+,sr
rts
conoutst: moveq #-1,d0
rts ; jdos requirement
***********************************************************************************
* *
* routine to set up the general interrupt port registers *
* (gpip,are,ddr) *
* *
* algorithm to set up the port *
* *
* 1. mask off all interrupts via the imrx register, *
* 2. clear all enable and pending bits in the ierx and iprx *
* registers; *
* 3. check the inerrupt in-service registers and loop till *
* clear; *
* 4. init the aer register bits as desired (default = 11111111); *
* 5. init the ddr register bits as desired (default = 10000000); *
* 6. clear the gpip register; *
* 7. enable all desired interrupt enable bits; *
* 8. mask on all desired interrupt mask bits; *
* *
***********************************************************************************
initmfp: lea (gpip).w,a0 ; init mfp address pointer
moveq #0,d0 ; init to zero for clearing mfp
movep.l d0,0(a0) ; clear gpip thru iera
movep.l d0,8(a0) ; clear ierb thru isrb
movep.l d0,$10(a0) ; clear isrb thru vr
move.b #$48,$16(a0) ; set mfp autovector to $100 and s-bit
bset #2,2(a0) ; set cts to low to high transition
IF ROM_TOS306
lea (gpip_TT).w,a0 ; init mfp #2 address pointer
moveq #0,d0 ; init to zero for clearing mfp
movep.l d0,0(a0) ; clear gpip thru iera
movep.l d0,8(a0) ; clear ierb thru isrb
movep.l d0,$10(a0) ; clear isrb thru vr
move.b #$58,$16(a0) ; set mfp autovector to $140 and s-bit
ENDIF
clr.b (privexception).w
* init the "c" timer
move.w #$1111,(tc_rot).w ; setup bitstream for /4 on timer c interrupts
move.w #$14,(_timr_ms).w ; set timer calibration value
moveq #2,d0 ; set to timer C
moveq #$50,d1 ; set to /64 for 200 hz tick
move.w #$c0,d2 ; set to 192
bsr settimer ; setup timer and init interrupt vector.....
lea (timercint).l,a2 ; point to the timer C interrupt routine...
moveq #5,d0 ; point the the timer C interrupt number
bsr initint
* init the "d" timer
moveq #3,d0 ; select the d timer
moveq #1,d1 ; init for /4 for 9600 baud
moveq #2,d2 ; init for 9600 baud
bsr settimer ; branch to out timer initialier...
move.b #1,(rbufrec+34).w
* now init the 3 rs232 chip registers
lea (gpip).w,a0
move.l #$880105,d0
movep.l d0,$26(a0) ; inits scr,ucr,rsr,tsr
IF ROM_TOS306
* now init the 3 rs232 chip registers in mfp #2
lea (gpip_TT).w,a0
move.l #$880105,d0
movep.l d0,$26(a0) ; inits scr,ucr,rsr,tsr
move.b #1,(tcdcr_TT).w ; timer c+d control register: delay 1:4
move.b #2,(tddr_TT).w ; timer d data register
move.b #1,($1380).w ; ???
ENDIF
* check for SCC chip
tst.b (STEFlag).l
bne.s initmfp2
bsr initscc
* initialize the default rs-232 control line settings
initmfp2: bsr dtron
bsr rtson
* initialize the rs-232 buffer record structure
lea (rbufrec).w,a0
lea (rs232init).l,a1
moveq #$23,d0
bsr lbmove ; do block move and return
IF ROM_TOS306
lea (ttmfp_rbufrec).w,a0
lea (ttrs232init).l,a1
moveq #$23,d0
bsr lbmove
* ttrs232init:DC.L iobuf_ttrs232i ; ibufptr
* DC.W $0100 ; ibufsiz
* DC.W $0000 ; ibufhead
* DC.W $0000 ; ibuftail
* DC.W $0080 ; ibuflow
* DC.W $00c0 ; ibufhigh
* DC.L iobuf_ttrs232o ; obufptr
* DC.W $0100 ; obufsiz
* DC.W $0000 ; obufhead
* DC.W $0000 ; obuftail
* DC.W $0080 ; obuflow
* DC.W $00c0 ; obufhigh
* DC.B $00 ; rsrbyte
* DC.B $00 ; tsrbyte
* DC.B $00 ; rxoff
* DC.B $00 ; txoff
* DC.B $01 ; rsmode_xon
* DC.B $00 ; rsmode_filler
* DC.W $01ff
ENDIF
* initialize the midi buffer record structure
lea (mbufrec).w,a0
lea (minit).l,a1
moveq #$d,d0
bsr lbmove ; do block move and return
move.w #-1,(altdigits).l ; reset alt-numpad ascii entering
move.l #aciaexit,d0 ; init ikbd and midi error handler address
move.l d0,(vkbderr).w ; init keyboard error handler address
move.l d0,(vmiderr).w ; init midi error handler address
move.l #sysmidi,(midivec).w ; point to system midi interrupt vector
move.l #astatusmidi,(astatusmidip).w
move.l #astatuskeyboard,(astatuskeyp).w
move.l #itsakey,(itsakeyp).w
* init the midi acia next
move.b #$03,(midictl).w ; init the midi acia via master reset
* init the acia to divide by 16x clock, 8 bit data, 1 stop bit, no parity,
* rts low, transmitting interrupt disabled, receiving interrupt enabled
move.b #$95,(midictl).w
* initialize the keyboard acia interrupt vector exception address
move.b #7,(conterm).w ; enabled keyclick, repeat key, bell functions
move.l #jdostime,(clkintvec).w
move.l #genrts,d0 ; generalized rts for ikbd subsystems
move.l d0,(statintvec).w
move.l d0,(msintvec).w ; init user mouse interrupt adr to rts
move.l d0,(joyintvec).w
bsr initdevstables
*
* Sound routine initialization - uses the pre-init'ed d0.l=0000 !!
*
moveq #0,d0 ; init 'd0' to clear sound variables
move.l d0,(cursnd).w ; clear sound ptr
move.b d0,(timer).w ; clear delay timer
move.b d0,(auxd).w ; clear temp value
move.l d0,(prt_to).w ; init printer timeout to 0
bsr strobeoff ; init strobe to off (line high!)
move.b #15,(cdelay1).w ; init system default key repeat values
move.b #2,(cdelay2).w
* within the mouse relative routine
* initialize the ikbd buffer record structure
lea (kbufrec).w,a0
lea (kinit).l,a1
moveq #$d,d0
bsr.s lbmove ; do block move and return
bsr bioskeys ; point key translation address to
* the rom based translation tables
* init the acia next
move.b #$03,(keyctl).w ; init the acia via master Reset
* now that the vector is initialized, we can allow interrupts to occur!
* init the acia do divice by 64 clock, 8 bit data, 1 stop bit, no parity,
* rts low, transmitting interrupt disabled, receiving interrupt enabled
move.b #$96,(keyctl).w
movea.l #mfpvectr,a3 ; point to initializing array of exception vec's
moveq #3,d1 ; init branch counter/index
st1: move.l d1,d2
move.l d1,d0 ; load in interrupt # to setup
addi.b #9,d0 ; add constand to point to proper mfp interrupt
asl.l #2,d2
movea.l (a3,d2.w),a2
bsr initint ; go to service routine
dbra d1,st1
lea (midikey).l,a2
moveq #6,d0 ; load in interrupt # to setup
bsr initint ; go to service routine
* initializing code which sets the enable
* and mask bits...
lea (ctsint).l,a2 ; point to the CTS interrupt routine...
moveq #2,d0 ; point to the CTS interrrupt number
bsr initint
IF ROM_TOS306
movea.l #$e36704,a3 ; ttmfp_txerr,ttmfp_txrint,ttmfp_rxerr,ttmfp_rcvrint
movea.w #mfpTT_SendError,a0 ; $0164
move.l (a3)+,(a0)+ ; mfpTT_SendError = ttmfp_txer
move.l (a3)+,(a0)+ ; mfpTT_SendBufferEmpty = ttmfp_txrint
move.l (a3)+,(a0)+ ; mfpTT_ReceiveError = ttmfp_rxerr
move.l (a3)+,(a0)+ ; mfpTT_ReceiveBufferFull = ttmfp_rcvrint
ori.b #$1e,(iera_TT).w
ori.b #$1e,(imra_TT).w
ENDIF
genrts: rts
lbmove: move.b (a1)+,(a0)+
dbra d0,lbmove
rts
kinit: DC.L iobuf_keyb ; ibufptr
DC.W $0100 ; ibufsiz
DC.W $0000 ; ibufhead
DC.W $0000 ; ibuftail
DC.W $0040 ; ibuflow
DC.W $00c0 ; ibufhigh
minit: DC.L iobuf_midi ; ibufptr
DC.W $0080 ; ibufsiz
DC.W $0000 ; ibufhead
DC.W $0000 ; ibuftail
DC.W $0020 ; ibuflow
DC.W $0060 ; ibufhigh
rs232init: DC.L iobuf_rs232i ; ibufptr
DC.W $0100 ; ibufsiz
DC.W $0000 ; ibufhead
DC.W $0000 ; ibuftail
DC.W $0080 ; ibuflow
DC.W $00c0 ; ibufhigh
DC.L iobuf_rs232o ; obufptr
DC.W $0100 ; obufsiz
DC.W $0000 ; obufhead
DC.W $0000 ; obuftail
DC.W $0080 ; obuflow
DC.W $00c0 ; obufhigh
DC.B $00 ; rsrbyte
DC.B $00 ; tsrbyte
DC.B $00 ; rxoff
DC.B $00 ; txoff
DC.B $01 ; rsmode_xon
DC.B $00 ; rsmode_filler
DC.W $01ff
* array of exception vector addresses for the above interrupts, including
* dummy vectors that point to "rte's".
mfpvectr: DC.L txerror
DC.L txrint
DC.L rxerror
DC.L rcvrint
***********************************************************************************
* *
* routine to set up a timer *
* *
* algorithm to init a timer *
* *
* 1. determine which timer and set d0.b = to timer's index value *
* as shown below; *
* 2. disable the associated interrupt; *
* 3. disable the timer itself via it's timer control register; *
* 4. initialize the timer's data register *
* 5. repeat step #4 until the data register's contents are *
* verified, per the errata sheet to the 68901 description; *
* 6. turn on the timer by using the value that you previously *
* stored in d1; *
* *
* note: the interrupt vector for the associated timer *
* is not set in this routine, so it is the user's *
* responsiblity to set it if so desired! *
* *
* *
* registers used: d0-d3/a0-a3 *
* registers saved: d0-d3/a0-a3 *
* entry: *
* d0.l - timer to be set *
* 0 - timer a *
* 1 - timer b *
* 2 - timer c *
* 3 - timer d *
* d1.b - timer's new control setting *
* d2.b - timer's data register data *
* *
* exit: no values to pass *
* *
* d3 - used and abused by call to mskreg routine *
* a0.l - set to mfp register base *
* a1.l - temporary location for a3 *
* a2.l - used to pass table address to mskreg routine *
* a3.l - used to pass table address to mskreg routine *
* *
***********************************************************************************
settimer: movem.l d0-d4/a0-a3,-(sp) ; save all registers to be messed with!!
movea.w #gpip,a0 ; set mfp chip address pointer
movea.l #imrt,a3 ; mask off the timer's interrupt maskable bit
movea.l #imrmt,a2
bsr.s mskreg
movea.l #iert,a3 ; mask off the timer's interrupt enable bit
movea.l #imrmt,a2
bsr.s mskreg
movea.l #iprt,a3 ; mask off the timer's interrupt pending bit
movea.l #imrmt,a2
bsr.s mskreg
movea.l #isrt,a3 ; mask off the timer's interrupt inservice bit
movea.l #imrmt,a2
bsr.s mskreg
movea.l #tcrtab,a3 ; mask off the timer's control bits
movea.l #tcrmsk,a2
bsr.s mskreg
exg a3,a1 ; save address pointer for restoring control
lea (tdrtab).l,a3 ; initialize the timer data register
moveq #0,d3 ; to prevent false effective address generation
move.b (a3,d0.w),d3
verify: move.b d2,(a0,d3.w)
cmp.b (a0,d3.w),d2
bne.s verify
exg a3,a1 ; grab that register address back
or.b d1,(a3) ; mask the timer control register value
movem.l (sp)+,d0-d4/a0-a3 ; restore all registers that were saved
rts
***********************************************************************************
* generalize mask register bit(s) routine *
* *
* entry *
* static d0 - contains the timer # *
* d3 - used and abused *
* d4 - used and abused *
* static a0 - mfp register base *
* a3 - points to table of similar timer registers *
* static a2 - points to table of similar timer data registers *
***********************************************************************************
mskreg: bsr.s getmask
move.b (a2),d3 ; grab mask now
and.b d3,(a3) ; and have masked off the desired bit(s)
rts
getmask: moveq #0,d3 ; to prevent false effective address generation
adda.w d0,a3 ; have got pointer to mfp register now
move.b (a3),d3 ; now have the address offset to mfp
add.l a0,d3
movea.l d3,a3 ; now have address pointing to desired mfp reg.
* now we get the mask to turn off interrupt
adda.w d0,a2 ; have got pointer to mask now
rts
iert: DC.B $06,$06,$08,$08
iprt: DC.B $0a,$0a,$0c,$0c
isrt: DC.B $0e,$0e,$10,$10
imrt: DC.B $12,$12,$14,$14
imrmt: DC.B $df,$fe,$df,$ef
tcrtab: DC.B $18,$1a,$1c,$1c
tcrmsk: DC.B $00,$00,$8f,$f8
tdrtab: DC.B $1e,$20,$22,$24
***********************************************************************************
* initialize mfp interrupt via GEMDOS *
* *
* entry *
* *
* void mfpint(numint,intvec) *
* word numint *
* long intvec *
* *
***********************************************************************************
mfpint: move.w 4(sp),d0
movea.l 6(sp),a2
andi.l #$f,d0 ; to ensure masking of 0-$f
***********************************************************************************
* *
* routine to init an mfp associated interrupt vector *
* *
* algorithm *
* *
* 1. block the interrupt via it's mask bit; *
* 2. disable the interrupt's enable and pending bits; *
* 3. check the interrupt's in-service register and loop till *
* clear; *
* 4. init the interrupt's associated vector; *
* 5. set the interrupt's enable bit; *
* 6. set the interrupt's mask bit; *
* *
* entry *
* d0 - contains interrupt # to aff *
* a2 - contains new vector address *
***********************************************************************************
initint: movem.l d0-d2/a0-a2,-(sp) ; save affected registers
bsr.s disint ; disable the interrupts
move.l d0,d2 ; get a copy so as to determine where to...
asl.w #2,d2 ; place the a2 address into the int. vector
addi.l #$100,d2 ; interrupt vector addr = (4 * int) + $000100
movea.l d2,a1 ; transfer the calculated address to a register
move.l a2,(a1) ; ...that can act upon it thus<--vector init'ed
bsr.s enabint ; enable interrupts
movem.l (sp)+,d0-d2/a0-a2 ; restore affected registers
rts
*************************************************************************
* disable an mfp interrupt via GEMDOS *
* *
* entry *
* *
* void jdisint(numint) *
* word numint *
* *
*************************************************************************
jdisint: move.w 4(sp),d0
andi.l #$f,d0 ; to ensure masking of 0-$f
*************************************************************************
* interrupt disable routine *
*************************************************************************
disint: movem.l d0-d1/a0-a1,-(sp) ; save affected registers
lea (gpip).w,a0 ; set mfp chip address pointer
lea $12(a0),a1 ; set a1 to the mskoff routine
bsr.s bselect ; generate the appropriate bit to clear
bclr d1,(a1) ; and clear the bit...
lea 6(a0),a1 ; set a1 for another mskoff call
bsr.s bselect
bclr d1,(a1) ; and clear the bit...
lea $e(a0),a1 ; now set up to check for interrupts in progress
bsr.s bselect ; get proper a/b version...
move.b #$fe,d0
rol.b d1,d0
move.b d0,(a1)
movem.l (sp)+,d0-d1/a0-a1 ; restore affected registers
rts
*************************************************************************
* enable/re-enabled an mfp interrupt via GEMDOS *
* *
* entry *
* *
* void jenabint(numint) *
* word numint *
* *
*************************************************************************
jenabint: move.w 4(sp),d0
andi.l #$f,d0 ; to ensure masking of 0-$f
*************************************************************************
* enable interrupt routine *
*************************************************************************
enabint: movem.l d0-d1/a0-a1,-(sp) ; save affected registers
lea (gpip).w,a0 ; set mfp chip address pointer
lea 6(a0),a1 ; set up to enable the interrupt enable bit
bsr.s bselect
bset d1,(a1) ; and set the bit...
lea $12(a0),a1 ; set up to enable the interrupt mask bit
bsr.s bselect
bset d1,(a1) ; and set the bit...
movem.l (sp)+,d0-d1/a0-a1 ; restore affected registers
rts
*************************************************************************
* *
* the following routine generates the appropriate bset/bclr # *
* for the interrupt # specified in d0. valid interrupt #'s are *
* 0 --> 15 as shown in the 68901 chip specification. It also *
* selects between the ixra and the ixrb version of the register *
* as is appropriate. *
* *
* entry d0 - contains the interrupt number *
* a1 - contains the pointer to the "ixra" version of *
* the interrupt byte to mask *
* exit d0 - same as upon entry *
* d1 - contains the number of the bit *
*************************************************************************
bselect: move.b d0,d1 ; copy d0 to d1 for scratch work
cmpi.b #8,d1 ; see if desired int # >= 8...
blt.s skip0 ; ...and branch if it ain't...
subq.w #8,d1 ; adjust for using ixrb instead
rts
skip0: addq.l #2,a1 ; adjust for using ixrb instead
rts
*************************************************************************
* *
* receiver buffer full interrupt routine *
* *
* grabs data from the rs-232 receiver port *
* *
*************************************************************************
rcvrint: movem.l d0-d1/a0-a2,-(sp) ; save affected registers
lea (rbufrec).w,a0 ; point to current output buffer record
lea (gpip).w,a2 ; set mfp chip address pointer
_rcvrint: move.b $2a(a2),$1c(a0) ; do the required rsr read before the udr read!
move.b $2e(a2),d0 ; get incoming data byte
btst #0,$20(a0) ; is the rs232 mode xon/xoff?
beq.s ri3 ; no... so process normally
cmpi.b #$13,d0 ; is the data an "xoff" signal?
bne.s ri2 ; no...now check for xon
move.b #$ff,$1f(a0) ; set to halted transmission to host
bra.s ri8
ri2: cmpi.b #$11,d0 ; is the data an "xon" signal?
bne.s ri3 ; neither xon/xoff value, must be normal data...
clr.b $1f(a0) ; set to normal transmission status
bra.s ri7 ; abnormal exit condition!!
ri3: move.w 8(a0),d1 ; get current tail pointer offset
bsr wrapin ; do wrap of input pointer if needed
cmp.w 6(a0),d1 ; head=tail?
beq.s ri8 ; yes...exit...
bsr rs232put ; store data in ring buffer
* now check for highwater mark triggering of flow-control
tst.b $20(a0) ; flow control active?
beq.s ri8 ; no...exit...
move.w 8(a0),d0 ; current tail pointer
sub.w 6(a0),d0 ; - current head
bpl.s ri4 ; wrap around?
add.w 4(a0),d0 ; + size to correct the wrap around
ri4: cmp.w $c(a0),d0 ; high-water mark not reached?
blt.s ri8 ; correct...exit...
tst.b $1e(a0) ; high-water flag already set?
bne.s ri8 ; yes...exit...
move.b #$ff,$1e(a0) ; set high-water flag
btst #0,$20(a0) ; are we using xon/xoff flow control?
bne.s ri6 ; yes...
bsr rtsoff ; we're ready now for more data...yum! yum!
bra.s ri8
ri6: move.b #$13,$21(a0) ; xoff
ri7: tst.b $2c(a2) ; transmitter status?
bpl.s ri8 ; not ready
bsr wr_rs232
ri8: move.b #$ef,$e(a2)
movem.l (sp)+,d0-d1/a0-a2 ; restore affected registers
rte
*************************************************************************
* *
* transmit buffer empty interrupt routine *
* *
*************************************************************************
txrint: movem.l d0-d1/a0-a2,-(sp) ; save affected registers
lea (gpip).w,a2 ; set mfp chip address pointer
lea (rbufrec).w,a0 ; point to current output buffer record
_txrint: bsr wr_rs232
move.b #$fb,$e(a2) ; xmit buffer empty
movem.l (sp)+,d0-d1/a0-a2 ; restore affected registers
rte
*************************************************************************
* *
* Clear-To-Send interrupt routine *
* *
*************************************************************************
ctsint: movem.l d0-d1/a0-a2,-(sp) ; save affected registers
lea (rbufrec).w,a0 ; point to current output buffer record
lea (gpip).w,a2 ; set mfp chip address pointer
btst #1,$20(a0) ; are we using CTS/RTS flow control?
beq.s ctsexit ; no...
btst #2,(a2) ; CTS already active?
bne.s ctsoff ; yes...
clr.b $1f(a0) ; xon_flag = 0
bset #2,2(a2) ; CTS set
tst.b $2c(a2) ; transmitter status?
bpl.s ctsexit ; not empty...
bsr.s wr_rs232 ; send a byte
bra.s ctsexit
ctsoff: move.b #$ff,$1f(a0) ; xon_flag = -1
bclr #2,2(a2)
ctsexit: move.b #$fb,$10(a2) ; clear CTS
movem.l (sp)+,d0-d1/a0-a2 ; restore affected registers
rte
*************************************************************************
* routines to handle tx or rx errors *
*************************************************************************
rxerror: movem.l d0/a0,-(sp) ; save all registers
lea (gpip).w,a0 ; set mfp chip address pointer
_rxerror: move.b $2a(a0),(rbufrec+28).l ; receiver status register
move.b $2e(a0),d0 ; dummy read of data register
move.b #$f7,$e(a0)
movem.l (sp)+,d0/a0 ; restore all registers
rte
txerror: move.l a0,-(sp)
lea (gpip).w,a0 ; set mfp chip address pointer
_txerror: move.b $2c(a0),(rbufrec+29).l ; transmitter status register
move.b #$fd,$e(a0)
movea.l (sp)+,a0 ; restore all registers
tst.b (tsr).w
rte
wr_rs232: move.l a0,-(sp) ; save all registers
move.b $21(a0),d0 ; send handshake byte pending?
beq.s wr_rs2322 ; no...
clr.b $21(a0) ; clear handshake byte
bra.s wr_rs232o ; output the handshake byte
wr_rs2322: move.b $20(a0),d0 ; flow control active?
and.b $1f(a0),d0 ; and xon-flag
bne.s wr_rs2324 ; yes...
adda.w #$e,a0 ; switch to output ring buffer
move.w 6(a0),d0 ; head-index
cmp.w 8(a0),d0 ; = tail-index?
beq.s wr_rs2324 ; buffer empty? => out
bsr.s rs232get ; get next character from the buffer
wr_rs232o: tst.b $2c(a2) ; transmit buffer empty?
bpl.s wr_rs232o ; no...wait...
move.b $2c(a2),(rbufrec+29).l ; save transmitter status
move.b d0,$2e(a2) ; output the byte
wr_rs2324: movea.l (sp)+,a0 ; restore all registers
rts
rs232put: move.w 8(a0),d1 ; tail-index
bsr.s wrapin
rs232putl: cmp.w 6(a0),d1 ; = head-index?
beq.s rs232putl ; buffer full...wait...
movea.l (a0),a1 ; ptr to send buffer
and.l #$ffff,d1
move.b d0,(a1,d1.l) ; byte into the send buffer
move.w d1,8(a0) ; update tail-index
rts
rs232get: move.w 6(a0),d1 ; head-index
cmp.w 8(a0),d1 ; = tail-index?
beq.s rs232get ; empty? wait...
bsr.s wrapin
movea.l (a0),a1 ; ptr to receive buffer
moveq #0,d0
and.l #$ffff,d1
move.b (a1,d1.l),d0 ; byte from the receive buffer
move.w d1,6(a0) ; update head-index
rts
rtson: lea (psgsel).w,a1
move sr,-(sp)
ori #$700,sr
move.b #14,(a1)
move.b (a1),d1
and.b #$f7,d1 ; clear bit 3 in port A
move.b d1,2(a1)
move (sp)+,sr
rts
rtsoff: lea (psgsel).w,a1
move sr,-(sp)
ori #$700,sr
move.b #14,(a1)
move.b (a1),d1
ori.b #8,d1 ; set bit 3 in port A
move.b d1,2(a1)
move (sp)+,sr
rts
wrapin: addq.w #1,d1 ; i=h+1
cmp.w 4(a0),d1 ; > i => current bufsiz?
bcs.s wi1 ; no...
moveq #0,d1 ; wrap pointer
wi1: rts
iorec: move.w 4(sp),d1 ; device number
beq.s iorecaux ; rs232? =>
asl.l #2,d1
move.l devtab(pc,d1.w),d0
rts
iorecaux: move.l (bsrecauxp).w,d0
rts
devtab: DC.L rbufrec ; RS232
DC.L kbufrec ; IKBD
DC.L mbufrec ; MIDI
rsconf: movea.l (bsrsconfp).w,a0
jmp (a0)
auxrsconf: lea (rbufrec).w,a0 ; point to current output buffer record
lea (gpip).w,a2 ; set mfp chip address pointer
_auxrsconf: moveq #0,d0
cmpi.w #-2,4(sp) ; -2 = return last baudrate
bne.s auxrsconf1
move.b $22(a0),d0 ; last baudrate
rts
auxrsconf1: ori #$700,sr ; no interrupts for now
*
* first we grab the old ucs,rsr,tsr,scr contents
*
movep.l $28(a2),d7
*
* set flow control mode(s)
*
move.w 6(sp),d0 ; if -1 then don't change
cmp.w #3,d0
bhi.s auxc1 ; no =>
bne.s auxrsconf2
moveq #1,d0 ; set flag for handshake
auxrsconf2: cmp.b $20(a0),d0 ; state unchanged? => continue
beq.s auxc1
move.w d0,-(sp) ; save new handshake state
tst.b $1f(a0) ; xon flag set?
beq.s auxrsconf3
clr.b $1f(a0) ; clear xon flag
bsr wr_rs232 ; transmit xon to continue
auxrsconf3: tst.b $1e(a0) ; RTS set?
beq.s auxrsconf4 ; no...
bsr auxinclrhw ; reenable RTS
auxrsconf4: move.w (sp)+,d0 ; new handshake state
move.b d0,$20(a0) ; set new handshake state
*
* set timer baud rate
*
auxc1: tst.w 4(sp) ; if -1 then don't change
bmi.s auxc2
bclr #0,$2a(a2) ; disable receiver
bclr #0,$2c(a2) ; disable transmitter
move.w 4(sp),d1 ; new baudrate
move.b d1,$22(a0) ; store for later
move.b baudctrl(pc,d1.w),d0 ; get baudrate control register settings mask
move.b bauddata(pc,d1.w),d2 ; get baudrate data register value
andi.b #$70,$1c(a2)
move.b d2,$24(a2) ; set tuner D to new baud rate
or.b d0,$1c(a2)
bset #0,$2a(a2) ; enable receiver
bset #0,$2c(a2) ; enable transmitter
* set rs-232 registers
auxc2: tst.w 8(sp) ; if -1 then don't change
bmi.s auxc3
move.b 9(sp),$28(a2) ; set ucr
auxc3: tst.w $a(sp) ; if -1 then don't change
bmi.s auxc4
move.b $b(sp),$2a(a2) ; set rsr
auxc4: tst.w $c(sp) ; if -1 then don't change
bmi.s auxc5
move.b $d(sp),$2c(a2) ; set tsr
auxc5: tst.w $e(sp) ; if -1 then don't change
bmi.s auxc6
move.b $f(sp),$26(a2) ; set scr
auxc6: move.l d7,d0 ; move old contents of rs-232 registers to d0.l
rts
* baudrate table - control register setting
baudctrl: DC.B $01,$01,$01,$01,$01,$01,$01,$01
DC.B $01,$01,$01,$01,$01,$01,$02,$02
* baudrate table - data register setting
bauddata: DC.B $01,$02,$04,$05,$08,$0a,$0b,$10
DC.B $20,$40,$60,$80,$8f,$af,$40,$60
rs232devs: DC.L auxistat ; Dev 6: "Modem 1" - ST MFP
DC.L auxin
DC.L _auxostat
DC.L _auxout
DC.L auxrsconf
DC.L rbufrec
DC.L sccbistat ; Dev 7: "Modem 2" - SCC Channel B
DC.L sccbin
DC.L sccbostat
DC.L sccbout
DC.L sccbrsconf
DC.L sccbbufrec
IF TOS_ROM306
DC.L ttmfp_auxistat ; Dev 8: "Seriell 1" - TT MFP
DC.L ttmfp_auxin
DC.L ttmfp__auxostat
DC.L ttmfp__auxout
DC.L ttmfp_auxrsconf
DC.L ttmfp_rbufrec
ENDIF
DC.L sccaistat ; Dev 8/9: "Seriell 2" - SCC Channel A
DC.L sccain
DC.L sccaostat
DC.L sccaout
DC.L sccarsconf
DC.L sccabufrec
_bconmap: moveq #0,d0
move.w 4(sp),d1
move.w (bdevmappedAux).w,d0
cmp.w #-1,d1
beq.s _bconmap_rts
move.l #bdevtabptr,d0
cmp.w #-2,d1
beq.s _bconmap_rts
moveq #0,d0
subq.w #6,d1
bmi.s _bconmap_rts
cmp.w (bdevcount).w,d1
bcc.s _bconmap_rts
move.w (bdevmappedAux).w,d1
subq.w #6,d1
asl.w #3,d1
move.w d1,d2
add.w d1,d1
add.w d1,d2
movea.l (bdevtabptr).w,a0
adda.w d2,a0
lea (xconstat+4).w,a1
move.l (a1),(a0)+
move.l $20(a1),(a0)+
move.l $40(a1),(a0)+
move.l $60(a1),(a0)+
move.l (bsrsconfp).l,(a0)+
move.l (bsrecauxp).w,(a0)+
move.w 4(sp),d1
subq.w #6,d1
asl.w #3,d1
move.w d1,d2
add.w d1,d1
add.w d1,d2
movea.l (bdevtabptr).w,a0
adda.w d2,a0
move.l (a0)+,(a1)
move.l (a0)+,$20(a1)
move.l (a0)+,$40(a1)
move.l (a0)+,$60(a1)
move.l (a0)+,(bsrsconfp).w
move.l (a0)+,(bsrecauxp).w
move.w (bdevmappedAux).w,d0
move.w 4(sp),(bdevmappedAux).w
_bconmap_rts:rts
initdevstables:lea (bdevtable).w,a0
move.l a0,(bdevtabptr).w
IF TOS_ROM306
move.w #4,(bdevcount).w
ELSE
move.w #3,(bdevcount).w
moveq #1,d0 ; only MFP RS232 is available
movea.l sp,a1
movea.l (busexception).w,a2
move.l #initdevnoscc,(busexception).w
tst.b (scu_gp1).w
move.w #3,d0 ; also 2 SCC RS232 are available
initdevnoscc:movea.l a1,sp
move.l a2,(busexception).w
move.w d0,(bdevcount).w
ENDIF
lea rs232devs(pc),a1
IF TOS_ROM306
move.w #$17,d0 ; 4 devices
ELSE
move.w #$11,d0 ; 3 devices
ENDIF
initdev1: move.l (a1)+,(a0)+
dbra d0,initdev1
move.w #6,(bdevmappedAux).w ; Device 6 is mapped into device 1 (Aux)
lea (rs232devs).l,a1 ; Map MFP RS232 to dev 1
lea (xconstat+4).w,a0
move.l (a1)+,(a0) ; Fix tconstat for dev 1
move.l (a1)+,$20(a0) ; Fix tconin for dev 1
move.l (a1)+,$40(a0) ; Fix tcostat for dev 1
move.l (a1)+,$60(a0) ; Fix tconout for dev 1
move.l (a1)+,(bsrsconfp).w
move.l (a1)+,(bsrecauxp).w
rts
IF ROM_TOS306
ttmfp_auxistat:
lea (ttmfp_rbufrec).w,a0
bra _auxistat
ttmfp_auxin:lea (ttmfp_rbufrec).w,a0
lea (gpip_TT).w,a2
bra _auxin
ttmfp__auxostat:
lea (ttmfp_rbufrec+14).w,a0
bra __auxostat
ttmfp__auxout:
move.w 6(sp),d0
lea (ttmfp_rbufrec+14).w,a0
bsr rs232put
lea (ttmfp_rbufrec).w,a0
lea (gpip_TT).w,a2
bra _auxoutwr
ttmfp_rcvrint:movem.l d0-d1/a0-a2,-(sp)
lea (ttmfp_rbufrec).w,a0
lea (gpip_TT).w,a2
bra _rcvrint
ttmfp_txrint:movem.l d0-d1/a0-a2,-(sp)
lea (gpip_TT).w,a2
lea (ttmfp_rbufrec).w,a0
bra _txrint
ttmfp_rxerr:movem.l d0/a0,-(sp)
lea (gpip_TT).w,a0
bra _rxerror
ttmfp_txerr:move.l a0,-(sp)
lea (gpip_TT).w,a0
bra _txerror
ttmfp_auxrsconf:lea (ttmfp_rbufrec).w,a0
lea (gpip_TT).w,a2
bra _auxrsconf
ENDIF
sccinit: move.b (a1)+,d0 ; SCC register number
bmi.s sccina ; end of the register list? => exit
move.b d0,(a0) ; select register
move.b (a1)+,(a0) ; write into register
bra.s sccinit
sccina: rts
* SCC initialization
scctbl: DC.B $04,$44 ; x16, 1 stop, no parity
DC.B $01,$04
DC.B $02,$60
DC.B $03,$c0 ; rx 8 bit, no crc, rx disabled
DC.B $05,$e2 ; tx 8 bit, no crc, tx disabled, rts,dtr on
DC.B $06,$00
DC.B $07,$00
DC.B $09,$01
DC.B $0a,$00 ; NRZ
DC.B $0b,$50 ; tx & rx clk = brg, trxc=in
DC.B $0c,$18 ; clock divisor for 9600 (pclk)
DC.B $0d,$00 ; divisor high
DC.B $0e,$02 ; BRG = PCLK
DC.B $0e,$03 ; enable BRG
DC.B $03,$c1 ; enable Rx, 8 bits
DC.B $05,$ea ; enable Tx, 8 bits, rts, dtr on
DC.B $0f,$20
DC.B $00,$10
DC.B $00,$10
DC.B $01,$17
DC.B $09,$09
DC.B $ff ; end of the register list
DC.B $00
* disconnect LAN from port a
nolan: move sr,d1
ori #$700,sr
move.b #14,(psgsel).w
move.b (psgsel).w,d0
bset #7,d0
move.b #14,(psgsel).w
move.b d0,(psgwr).w
move d1,sr
rts
initscc: movea.l (busexception).w,a0
movea.l sp,a1
move.l #initscc_none,(busexception).w
tst.b (sccdat_a).w
move.l a0,(busexception).w
lea (SCC).w,a0
lea sccvect(pc),a1
moveq #15,d0
initscc2: move.l (a1)+,(a0)+
dbra d0,initscc2
IF ROM_TOS306
clr.w (scdmactl).w
ENDIF
bsr.s nolan
lea (sccabufrec).w,a0
lea sccbufinit(pc),a1
moveq #$25,d0
bsr lbmove
lea (sccbbufrec).w,a0
lea sccbufinit(pc),a1
moveq #$25,d0
bsr lbmove
move.l #$1434,(sccbbufrec).w
move.l #$1534,(sccbbufrec+14).w
lea (sccctl_a).w,a2
move.b #9,(a2)
move.b #$c0,(a2)
move.w #$104,d0 ; Delay Mode, /4 Prescale, data = 4 (about 6us delay)
jsr (mfpdelay).l
lea (sccctl_a).w,a0
lea scctbl(pc),a1
bsr sccinit
lea (sccctl_b).w,a0
lea scctbl(pc),a1
bsr sccinit
bset #5,(vme_mask).w ; Enable IRQ5 from VMEBUS/SCC
rts
initscc_none:move.l a0,(busexception).w
movea.l a1,sp
rts
**************************************************************************
* *
* 83C30 SCC interrupt vectors *
* *
**************************************************************************
sccvec_portbtrnbempty:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_b).w,a2
lea (sccbbufrec).w,a0
bra scc23
sccvec_portbextstchg:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_b).w,a2
lea (sccbbufrec).w,a0
bra scc24
sccvec_portbrecchr:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_b).w,a2
lea (sccbbufrec).w,a0
bra.s scc09
sccvec_portbschrrec:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_b).w,a2
lea (sccbbufrec).l,a0
bra scc16
sccvec_portatrnbempty:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_a).w,a2
lea (sccabufrec).w,a0
bra scc23
sccvec_portaextstchg:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_a).w,a2
lea (sccabufrec).w,a0
bra scc24
sccvec_portarecchr:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_a).w,a2
lea (sccabufrec).w,a0
bra.s scc09
sccvec_portaschrrec:movem.l d0-d1/a0-a2,-(sp)
lea (sccctl_a).w,a2
lea (sccabufrec).l,a0
bra scc16
scc09: move.b #8,(a2) ; select scc register #8
move.b (a2),d0 ; read "Receive Data register"
and.b $23(a0),d0
btst #0,$20(a0)
beq.s scc11
cmp.b #$13,d0
bne.s scc10
st $1f(a0)
bra.s scc15
scc10: cmp.b #$11,d0
bne.s scc11
tst.b $1f(a0)
sf $1f(a0)
bne.s scc14
scc11: move.w 8(a0),d1
bsr wrapin
cmp.w 6(a0),d1
beq.s scc15
bsr rs232put
tst.b $20(a0)
beq.s scc15
tst.b $1e(a0)
bne.s scc15
move.w 8(a0),d0
sub.w 6(a0),d0
bpl.s scc12
add.w 4(a0),d0
scc12: cmp.w $c(a0),d0
blt.s scc15
st $1e(a0)
btst #0,$20(a0)
bne.s scc13
move.b $1d(a0),d0
bclr #1,d0 ; clear RTS bit
move.b d0,$1d(a0)
move.b #5,(a2) ; select scc register #5
move.b d0,(a2) ; write "Transmit Parameters and Controls" register
bra.s scc15
scc13: move.b #$13,$21(a0)
scc14: move.b #0,(a2) ; select scc register #0
move.b (a2),d0 ; read register #0
btst #2,d0 ; Tx Buffer Empty?
beq.s scc15
bsr.s wr_scc
scc15: move.b #0,(a2) ; select scc register #0
move.b #$38,(a2) ; write "Reset Highest IUS Command" - clear pending interrupt
movem.l (sp)+,d0-d1/a0-a2
rte
scc16: move.b #1,(a2) ; select scc register #1
move.b (a2),d0 ; read special receive condition status bits
move.b #8,(a2) ; select scc register #8
move.b (a2),d0 ; read "Receive Data register"
move.b #0,(a2) ; select scc register #0
move.b #$30,(a2) ; write "Error Reset Command" - clear error condition
bra.s scc15
wr_scc: move.l a0,-(sp)
tst.b $21(a0)
bne.s scc19
IF ROM_STBOOK
btst #0,$20(a0)
beq.s scc18
tst.b $1f(a0)
bne.s scc22
scc18: btst #1,$20(a0)
beq.s scc19
move.b #0,(a2) ; select scc register #0
move.b (a2),d0 ; read register #0
btst #5,d0 ; CTS?
beq.s scc22
ELSE
move.b $20(a0),d0
and.b $1f(a0),d0
bne.s scc2
ENDIF
scc19: move.b #0,(a2) ; select scc register #0
move.b (a2),d0 ; read register #0
btst #2,d0 ; Tx Buffer Empty?
beq.s scc22
move.b $21(a0),d0
beq.s scc20
clr.b $21(a0)
bra.s scc21
scc20: adda.w #$e,a0
move.w 6(a0),d0
cmp.w 8(a0),d0
beq.s scc22
bsr rs232get
scc21: move.b #8,(a2) ; select scc register #8
move.b d0,(a2) ; write transmit buffer
scc22: movea.l (sp)+,a0
rts
scc23: move.b #0,(a2) ; select scc register #0
move.b #$28,(a2) ; write "Reset Tx Int Pending"
move.b #0,(a2) ; select scc register #0
move.b #$38,(a2) ; write "Reset Highest IUS Command" - clear pending interrupt
bsr.s wr_scc
movem.l (sp)+,d0-d1/a0-a2
rte
scc24: btst #1,$20(a0)
beq.s scc25
move.b #0,(a2) ; select scc register #0
move.b (a2),d0 ; read register #0
btst #5,d0 ; CTS?
seq $1f(a0)
beq.s scc25
IF ROM_STBOOK
bsr wr_scc
ELSE
bsr.s wr_scc
ENDIF
scc25: move.b #0,(a2) ; select scc register #0
move.b #$10,(a2) ; write "Reset Ext/Status Interrupts"
move.b #0,(a2) ; select scc register #0
move.b #$38,(a2) ; write "Reset Highest IUS Command" - clear pending interrupt
movem.l (sp)+,d0-d1/a0-a2
rte
**************************************************************************
* *
* scc BIOS callbacks *
* *
**************************************************************************
sccbistat: lea (sccbbufrec).w,a0
lea (sccctl_b).w,a2
bra.s sccistat
sccbin: lea (sccbbufrec).w,a0
lea (sccctl_b).w,a2
bra.s sccin
sccbostat: lea (sccbbufrec).w,a0
lea (sccctl_b).w,a2
bra.s _sccostat
sccbout: lea (sccbbufrec).w,a0
lea (sccctl_b).w,a2
bra _sccout
sccaistat: lea (sccabufrec).w,a0
lea (sccctl_a).w,a2
bra.s sccistat
sccain: lea (sccabufrec).w,a0
lea (sccctl_a).w,a2
bra.s sccin
sccaostat: lea (sccabufrec).w,a0
lea (sccctl_a).w,a2
bra.s _sccostat
sccaout: lea (sccabufrec).w,a0
lea (sccctl_a).w,a2
bra.s _sccout
**************************************************************************
* *
* scc port input status routine *
* *
**************************************************************************
sccistat: moveq #0,d0 ; set result to false
lea 6(a0),a1 ; head
lea 8(a0),a0 ; tail
cmpm.w (a0)+,(a1)+ ; atomic buffer empty test
beq.s sccist1
moveq #-1,d0 ; set result to true
sccist1: rts
**************************************************************************
* *
* scc port output status routine *
* *
**************************************************************************
_sccostat: move.w 8(a0),d1 ; tail
bsr wrapin
cmp.w 6(a0),d1 ; head
beq.s _sccost1
moveq #-1,d0 ; set result to true
rts
_sccost1: moveq #0,d0 ; set result to false
rts
**************************************************************************
* *
* scc input routine *
* *
**************************************************************************
sccin: bsr rs232get
move.w d0,-(sp)
tst.b $20(a0) ; flow control active?
beq.s sccinrts ; (no)
tst.b $1e(a0) ; high-water flag already set?
beq.s sccinrts ; no...exit...
move.w 8(a0),d0 ; tail
sub.w 6(a0),d0 ; - head
bpl.s sccin2 ; underflow?
add.w 4(a0),d0 ; + size
sccin2: cmp.w $a(a0),d0
bgt.s sccinrts
bsr.s sccinclrhw
sccinrts: move.w (sp)+,d0
rts
sccinclrhw: clr.b $1e(a0) ; clear high-water flag
btst #0,$20(a0) ; is the rs232 mode xon/xoff?
beq.s sccinclrhw2
move.b #$11,$21(a0) ; "xon"
bra.s _sccoutwr
sccinclrhw2:move.b $1d(a0),d0
bset #1,d0 ; set RTS
move.b d0,$1d(a0)
move.b #5,(a2) ; select scc register #5
move.b d0,(a2) ; write "Transmit Parameters and Controls" register
rts
**************************************************************************
* *
* scc output routine *
* *
**************************************************************************
_sccout: move.w 6(sp),d0 ; get data
adda.w #$e,a0
bsr rs232put ; exit via rs-232 output routine
suba.w #$e,a0
_sccoutwr: move.b #0,(a2) ; select scc register #0
move.b (a2),d0 ; read register #0
btst #2,d0 ; Tx Buffer Empty?
beq.s _sccoutrts ; not ready
move sr,-(sp)
ori #$700,sr
bsr wr_scc
move (sp)+,sr
_sccoutrts: rts
**************************************************************************
* *
* scc rsconf() routine *
* *
**************************************************************************
sccarsconf: lea (sccabufrec).w,a0 ; point to current output buffer record
lea (sccctl_a).w,a2
bra.s sccrsconf
sccbrsconf: lea (sccbbufrec).w,a0 ; point to current output buffer record
lea (sccctl_b).w,a2
sccrsconf: moveq #0,d0
cmpi.w #-2,4(sp) ; -2 = return last baudrate
bne.s sccrsconf2
move.b $22(a0),d0 ; last baudrate
rts
sccrsconf2: ori #$700,sr ; no interrupts for now
*
* first we grab the old ucs,rsr,tsr,scr contents
*
moveq #0,d7
move.b $1c(a0),d7
asl.w #8,d7
swap d7
move.b $1d(a0),d7
lsr.b #1,d7
and.b #4,d7
asl.w #8,d7
*
* set flow control mode(s)
*
move.w 6(sp),d0 ; if -1 then don't change
cmp.w #3,d0
bhi.s sccrsconf6
bne.s sccrsconf3
moveq #1,d0 ; set flag for handshake
sccrsconf3: cmp.b $20(a0),d0 ; state unchanged? => continue
beq.s sccrsconf6
tst.b $1f(a0) ; xon flag set?
beq.s sccrsconf4
clr.b $1f(a0) ; clear xon flag
bsr wr_scc
sccrsconf4: tst.b $1e(a0) ; RTS set?
beq.s sccrsconf5 ; no...
move.w d0,-(sp) ; save new handshake state
bsr sccinclrhw ; reenable RTS
move.w (sp)+,d0 ; new handshake state
sccrsconf5: move.b d0,$20(a0) ; set new handshake state
*
* set timer baud rate
*
sccrsconf6: move.w 4(sp),d0 ; new baudrate
cmp.w #$f,d0 ; if -1 (out of range 0..15) then don't change
bhi.s sccrsconf7
move.b d0,$22(a0) ; new baudrate
asl.w #1,d0
lea (sccbaudtab).l,a1
move.w (a1,d0.w),d0
move.b #$c,(a2) ; select scc register #12
move.b d0,(a2) ; write "Lower Byte of Baud Rate Generator Time Constant" register
lsr.w #8,d0
move.b #$d,(a2) ; select scc register #13
move.b d0,(a2) ; write "Upper Byte of Baud Rate Generator Time Constant" register
* set rs-232 registers
sccrsconf7: move.w 8(sp),d0 ; if -1 then don't change
bmi.s sccrsconf12
move.b d0,$1c(a0)
move.b d0,d1
and.b #$60,d1
lsr.b #5,d1
moveq #$ff,d2
lsr.b d1,d2
move.b d2,$23(a0)
move.b d0,d1
and.b #$60,d1
beq.s sccrsconf8
cmp.b #$60,d1
bne.s sccrsconf9
sccrsconf8: eori.b #$60,d1
sccrsconf9: move.b $1d(a0),d2
and.b #$9f,d2
or.b d1,d2
move.b d2,$1d(a0)
move.b #5,(a2) ; select scc register #5
move.b d2,(a2) ; write "Transmit Parameters and Controls" register
asl.b #1,d1
or.b #1,d1 ; "Rx Enable"
move.b #3,(a2) ; select scc register #3
move.b d1,(a2) ; write "Receive Parameters and Control" register
move.b d0,d1
and.b #$1e,d1
lsr.b #1,d1 ; mask stop bits (bit 2..3) and parity (bit 0..1)
bclr #1,d1
sne d2
bclr #0,d1 ; disable parity
bne.s sccrsconf10
bclr #1,d2 ; "odd parity"
bra.s sccrsconf11
sccrsconf10:bset #1,d2 ; "even parity"
sccrsconf11:and.b #3,d2 ; mask parity bits
or.b d2,d1
or.b #$40,d1 ; "X16 Clock Mode"
move.b #4,(a2) ; select scc register #4
move.b d1,(a2) ; write "Transmit/Receive Mis- cellaneous Parameters and Modes" register
sccrsconf12:
IF ROM_TOS306
move.w $a(sp),d0 ; if -1 then don't change
ELSE
move.w $c(sp),d0 ; if -1 then don't change
ENDIF
bmi.s sccrsconf14
btst #3,d0
beq.s sccrsconf13
bset #4,$1d(a0) ; set "Tx Enable"
bne.s sccrsconf14
move.b #5,(a2) ; select scc register #5
move.b $1d(a0),(a2) ; write "Transmit Parameters and Controls" register
bra.s sccrsconf14
sccrsconf13:bclr #4,$1d(a0) ; clear "Tx Enable"
beq.s sccrsconf14
move.b #5,(a2) ; select scc register #5
move.b $1d(a0),(a2) ; write "Transmit Parameters and Controls" register
sccrsconf14:move.l d7,d0 ; move old contents of rs-232 registers to d0.l
rts
sccbaudtab: DC.W $000b,$0018,$0032,$0044
DC.W $0067,$007c,$008a,$00d0
DC.W $01a1,$0345,$04e8,$068c
DC.W $074d,$08ee,$0d1a,$13a8
sccvect: DC.L sccvec_portbtrnbempty
DC.L $000000
DC.L sccvec_portbextstchg
DC.L $000000
DC.L sccvec_portbrecchr
DC.L $000000
DC.L sccvec_portbrecchr
DC.L $000000
DC.L sccvec_portatrnbempty
DC.L $000000
DC.L sccvec_portaextstchg
DC.L $000000
DC.L sccvec_portarecchr
DC.L $000000
DC.L sccvec_portarecchr
DC.L $000000
sccbufinit: DC.L $001210 ; ibufptr
DC.W $0100 ; ibufsiz
DC.W $0000 ; ibufhead
DC.W $0000 ; ibuftail
DC.W $0080 ; ibuflow
DC.W $00c0 ; ibufhigh
DC.L $001310 ; obufptr
DC.W $0100 ; obufsiz
DC.W $0000 ; obufhead
DC.W $0000 ; obuftail
DC.W $0080 ; obuflow
DC.W $00c0 ; obufhigh
DC.B $08 ; rsrbyte
DC.B $ea ; tsrbyte
DC.B $00 ; rxoff
DC.B $00 ; txoff
DC.B $01 ; rsmode_xon
DC.B $00 ; rsmode_filler
DC.W $01ff
*************************************************************************
* this code handles the midi/keyboard interrupt exception *
*************************************************************************
midikey: movem.l d0-d3/a0-a3,-(sp) ; save all registers
keymidi: movea.l (astatusmidip).w,a2
jsr (a2)
movea.l (astatuskeyp).w,a2
jsr (a2)
btst #4,(gpip).w ; check for pending interrupt occurance
beq.s keymidi ; repeat this interrupt processing
move.b #$bf,(isrb).w ; clear in-service bit
movem.l (sp)+,d0-d3/a0-a3 ; restore all registers
rte ; go back to what was happening!
astatusmidi:lea (mbufrec).w,a0
lea (midictl).w,a1
movea.l (vmiderr).w,a2
bra.s astatus2
astatuskeyboard:lea (kbufrec).w,a0
lea (keyctl).w,a1
movea.l (vkbderr).w,a2
astatus2: move.b (a1),d2 ; grab device status
btst #7,d2 ; make sure it was an interrupt request
beq.s aciaexit ; nope..it's empty
btst #0,d2 ; see if receiver buffer is full
beq.s mk1 ; nope..it's empty
movem.l d2/a0-a2,-(sp)
bsr.s arcvrint ; yes...get byte
movem.l (sp)+,d2/a0-a2
mk1: andi.b #$20,d2 ; mask off bits already tested
beq.s aciaexit ; see if any other status bits are on
move.b 2(a1),d0 ; grab data byte from acia data register
jmp (a2) ; yes to branch to pre-inited error subroutine
aciaexit: rts
*************************************************************************
* acia receiver buffer full interrupt routine *
*************************************************************************
arcvrint: move.b 2(a1),d0 ; grab data byte from acia data register
cmpa.l #kbufrec,a0
bne midibyte ; don't treat midi acia data as anything other than as pure data...
tst.b (kstate).w
bne.s ML3
cmpi.b #$f6,d0
bcc.s itsnotakey ; is it a ikbd header?
move.l (itsakeyp).w,-(sp) ; jump into ikbd processing function
rts
itsnotakey: subi.b #$f6,d0 ; generate true index into tables now
andi.l #$ff,d0 ; clear high 3 bytes for indexing
lea (ikbdev).l,a3 ; point to ikbd device state codes
move.b (a3,d0.w),(kstate).w ; set ikbd state
lea (ikbdlen).l,a3 ; point to ikbd device buffer length table
move.b (a3,d0.w),(kindex).w ; set ikbd device index counter
addi.w #$f6,d0 ; re-constitute original value
cmpi.b #$f8,d0 ; mouse position record?
blt.s ML8 ; no
cmpi.b #$fb,d0 ; mouse position record?
bgt.s ML8 ; no
move.b d0,(mousebuf).w ; store mouse byte
rts
ML8: cmpi.b #$fd,d0 ; joystick record?
blt.s ML8b ; no
move.b d0,(joyrec).w ; store joystick byte
ML8b: rts
ikbdev: DC.B $01,$02,$03,$03,$03,$03,$04,$05
DC.B $06,$07
ikbdlen: DC.B $07,$05,$02,$02,$02,$02,$06,$02
DC.B $01,$01
ML3: cmpi.b #6,(kstate).w
bcc ML35 ; a joystick 0/1 record byte, not both!
lea (ikbdparams).l,a2 ; point to ikbd subsystem parameters table
moveq #0,d2
move.b (kstate).w,d2 ; load to generate longword offset
subq.b #1,d2 ; d2 = (kstate-1) * 12
asl.w #1,d2
add.b (kstate).w,d2
subq.b #1,d2
asl.w #2,d2
movea.l (a2,d2.w),a0 ; load in subsystem's record pointer
movea.l 4(a2,d2.w),a1 ; load in subsystem's index base+record pointer
movea.l 8(a2,d2.w),a2 ; load in subsystem's pointer variable that contains the pointer to the subsystem's interrupt routine
movea.l (a2),a2 ; get the address of the interrupt routine
moveq #0,d2 ; clear out 'd2' for address manipulation
move.b (kindex).w,d2
suba.l d2,a1
move.b d0,(a1) ; store byte into the subsystems buffer
subq.b #1,(kindex).w ; decrement the number of bytes in the package
tst.b (kindex).w ; package received?
bne.s ML1 ; not yet...
ikserve: move.l a0,-(sp) ; stuff buffer pointer to stack
jsr (a2) ; go service the subsystem interrupt routine
addq.w #4,sp ; re-adjust stack
clr.b (kstate).w ; reset ikbd state
ML1: rts
ikbdparams: DC.L statrec
DC.L amrec
DC.L statintvec
DC.L amrec
DC.L mousebuf
DC.L msintvec
DC.L mousebuf
DC.L clkrec
DC.L msintvec
DC.L clkrec
DC.L joyrec
DC.L clkintvec
DC.L joyrec
DC.L joyrec+2
DC.L joyintvec
ML35: move.l #joyrec+1,d1
add.b (kstate).w,d1 ; kstate reflects joy0 or joy1 state
subq.b #6,d1
movea.l d1,a2 ; create index to joyrec table for record byte
move.b d0,(a2)
movea.l (joyintvec).w,a2 ; get user's joystick interrupt routine adr
lea (joyrec).w,a0 ; send along address of joystick data
bra.s ikserve
itsakey: move.b (kb_shift).w,d1 ; load in kbshift for manipulation
* check the special keys
cmpi.b #$2a,d0 ; left shift?
bne.s ari2
bset #1,d1
bra ari10
ari2: cmpi.b #$aa,d0
bne.s ari3
bclr #1,d1
bra ari10
ari3: cmpi.b #$36,d0 ; right shift?
bne.s ari4
bset #0,d1
bra.s ari10
ari4: cmpi.b #$b6,d0
bne.s ari5
bclr #0,d1
bra.s ari10
ari5: cmpi.b #$1d,d0 ; CTRL
bne.s ari6
bset #2,d1
bra.s ari10
ari6: cmpi.b #$9d,d0
bne.s ari7
bclr #2,d1
bra.s ari10
ari7: cmpi.b #$38,d0 ; ALT
bne.s ari8
bset #3,d1
bra.s ari10
ari8: cmpi.b #$b8,d0
bne.s ari9
bclr #3,d1
tst.w (altdigits).l ; ascii code via numpad active?
bmi.s ari10 ; no...
move.b d1,(kb_shift).w ; store shift status
move.l a0,-(sp)
moveq #0,d1 ; no scancode
move.w d1,d0 ; clear ascii code reg
move.b (altdigits+1).l,d0 ; take entered ascii code
move.w #-1,(altdigits).l ; reset alt-numpad ascii entering
bra conin25
ari9: cmpi.b #$3a,d0 ; CAPS LOCK
bne.s ari11
btst #0,(conterm).w
beq.s ari9a ; no click please!
movem.l d0-d2/a0-a2,-(sp)
movea.l (kcl_hook).w,a0
jsr (a0)
movem.l (sp)+,d0-d2/a0-a2
ari9a: bchg #4,d1 ; toggle CAPS LOCK state
ari10: move.b d1,(kb_shift).w ; restore new kbshift value
rts ; ignore CAPS LOCK break
ari11: btst #7,d0 ; is it a break code?
bne.s ari12 ; no... a break code!
move.b d0,(keyrep).w ; save for repeat purposes
move.b (cdelay1).l,(keydelay1).w
move.b (cdelay2).l,(keydelay2).w
bra.s ari16
ari12: move.b d0,d1
bclr #7,d1
cmp.b (keyrep).w,d1
bne.s ari15
moveq #0,d1
move.b d1,(keyrep).w
move.b d1,(keydelay1).w
move.b d1,(keydelay2).w
ari15: cmpi.b #$c7,d0 ; is it a "home" break-code?
beq.s ari18a ; yes...allow it to pass
cmpi.b #$d2,d0 ; is it an "insert" break-code?
bne ari14 ; no...regular break junk...
ari18a: btst #3,(kb_shift).w ; early "ALT" test to prevent double "nulls"
beq ari14 ; no ALT...so exit now...
ari16: btst #0,(conterm).w
beq.s ari16a ; no click please!
movem.l d0-d2/a0-a2,-(sp)
movea.l (kcl_hook).w,a1
jsr (a1)
movem.l (sp)+,d0-d2/a0-a2
ari16a: move.l a0,-(sp) ; store kbufrec pointer
moveq #0,d1
move.b d0,d1
default: use regular ascii table:movea.l (skeytran).w,a0
andi.w #$7f,d0
btst #4,(kb_shift).w ; caps-lock pressed?
beq.s conin21
movea.l (skeycl).w,a0 ; use caps-lock table
conin21: btst #0,(kb_shift).w
bne.s conin22
btst #1,(kb_shift).w
beq.s conin23
conin22: cmpi.b #$3b,d0 ; see if a possible function key
bcs.s conin22a ; unsigned less than lowest function scancode
cmpi.b #$44,d0 ; see if a possible function key
bhi.s conin22a ; unsigned greater than highest function scan
addi.w #$19,d1 ; add to change to GSX standard
moveq #0,d0 ; change to GSX standard
bra conin25
conin22a: movea.l (skeyshif).w,a0 ; use the shift table
conin23: move.b (a0,d0.w),d0
btst #2,(kb_shift).w ; is the control key down?
beq.s conin24a
cmpi.b #$d,d0 ; is it a carriage return?
bne.s conin23a
moveq #$a,d0 ; change to a linefeed according to GSX spec...
beq.s conin24x
conin23a: cmpi.b #$47,d1 ; convert CONTROL-home to gsx standard
bne.s conin23b
addi.w #$30,d1 ; by adding #$30...
bra conin25
conin23b: cmpi.b #$4b,d1 ; convert CONTROL-left arrow to gsx standard
bne.s conin23c
moveq #$73,d1 ; change according to gsx spec
moveq #0,d0
bra conin25
conin23c: cmpi.b #$4d,d1 ; convert CONTROL-right arrow to gsx standard
bne.s conin24x
moveq #$74,d1 ; change according to gsx spec
moveq #0,d0
bra conin25
conin24x: cmpi.b #$32,d0 ; Control-M?
bne.s conin240
moveq #0,d0 ; ascii code 0
bra conin25
conin240: cmpi.b #$36,d0 ; Control-Shift-Right?
bne.s conin241
moveq #$1e,d0 ; ascii code RS
bra conin25
conin241: cmpi.b #$2d,d0 ; Control-X?
bne.s conin24a
moveq #$1f,d0 ; ascii code US
bra conin25
* this routine allows entering any ascii code by using the numpad
* and holding down ALT during it.
conin24a: btst #3,(kb_shift).w ; is the alt key down?
beq conin2x
cmp.b #$67,d1 ; key 0 ... 9 on the number pad?
bcs.s outside ; no...
cmp.b #$70,d1
bhi.s outside ; no...
move.w (altdigits).l,d0 ; already entering an ascii code?
bpl.s conin24b ; yes
moveq #0,d0 ; start with 0
conin24b: mulu.w #10,d0 ; previous digit * 10
ext.w d1
move.b (a0,d1.w),d1 ; ascii code of current digit
sub.b #'0',d1 ; convert into number
add.b d1,d0 ; plus old number
move.w d0,(altdigits).l ; store as new number code
movea.l (sp)+,a0
rts
outside: cmpi.b #$62,d1 ; is it an "alt help" signal to dump the screen?
bne.s alt15a ; no...
addq.w #1,(_prtcnt).w ; yes...switch the signal flag on!...
movea.l (sp)+,a0 ; restore kbufrec pointer
bra ari14 ; ...and exit
*
* check the alt-insert/alt-home key make/break combinations, first
*
alt15a: lea (mousekey1).l,a2 ; get pointer to first alt. mouse scancode table
moveq #3,d2 ; create countdown
mkloop1: cmp.b (a2,d2.w),d1 ; is table's scancode value = current value?
beq keymaus1 ; yes...go preprocess it...
dbra d2,mkloop1 ; no...loop back to check next table value
cmpi.b #$48,d1 ; is it an up arrow?
bne.s alt11
move.b #0,d1 ; x value for up arrow
move.b #$f8,d2 ; y value for up arrow
move.b (kb_shift).w,d0 ; grab current setting
andi.b #3,d0 ; KBRSH+KBLSH bits
beq keymaus
move.b #$ff,d2 ; y value for up arrow
bra keymaus
alt11: cmpi.b #$4b,d1 ; is it an left arrow?
bne.s alt12
move.b #0,d2 ; x value for left arrow
move.b #$f8,d1 ; y value for left arrow
move.b (kb_shift).w,d0 ; grab current setting
andi.b #3,d0 ; KBRSH+KBLSH bits
beq keymaus
move.b #$ff,d1 ; x value for left arrow
bra keymaus
alt12: cmpi.b #$4d,d1 ; is it an right arrow?
bne.s alt13
move.b #8,d1 ; x value for right arrow
move.b #0,d2 ; y value for right arrow
move.b (kb_shift).w,d0 ; grab current setting
andi.b #3,d0 ; KBRSH+KBLSH bits
beq keymaus
move.b #1,d1 ; x value for right arrow
bra keymaus
alt13: cmpi.b #$50,d1 ; is it an down arrow?
bne.s alt14
move.b #0,d1 ; x value for down arrow
move.b #8,d2 ; y value for down arrow
move.b (kb_shift).w,d0 ; grab current setting
andi.b #3,d0 ; KBRSH+KBLSH bits
beq keymaus
move.b #1,d2 ; y value for down arrow
bra keymaus
alt14: btst #2,(kb_shift).w ; CTRL key?
bne.s conin24 ; yes
cmpi.b #2,d1
bcs.s alt1 ; not >= the '1' key scancode
cmpi.b #$d,d1
bhi.s alt1 ; not <= the '=' key scancode
addi.b #$76,d1 ; scancode is a key between '1' key and '=' key
bra.s alt2
alt1: cmpi.b #$41,d0 ; is the key an ascii 'A' or greater?
bcs.s alt3 ; no skip to check if 'a'-'z'...
cmpi.b #$5a,d0 ; is the key an ascii 'Z' or less?
bhi.s alt3 ; no skip to check if 'a'-'z'...
alt2: moveq #0,d0
bra.s conin25
alt3: cmpi.b #$61,d0 ; is the key an ascii 'a' or greater?
bcs.s conin25 ; not...skip to finish normal processing
cmpi.b #$7a,d0 ; is the key an ascii 'z' or less?
bhi.s conin25 ; not...skip to finish normal processing
bra.s alt2
conin2x: btst #2,(kb_shift).w ; CTRL key?
beq.s conin25 ; no
conin24: andi.w #$1f,d0 ; yep, so CTRLize the key
conin25: asl.w #8,d1 ; shift the scan code to the word's high byte
add.w d1,d0 ; form the outgoing word
movea.l (sp)+,a0 ; restore kbufrec pointer
move.w 8(a0),d1 ; get current tail pointer offset
addq.w #4,d1 ; index = tail + 4
cmp.w 4(a0),d1 ; check to see if buffer should wrap
bcs.s ari13 ; no...
moveq #0,d1 ; wrap pointer
ari13: cmp.w 6(a0),d1 ; head=tail?
beq.s ari14 ; yes
movea.l (a0),a2 ; get buffer pointer
* move kb_shift into the upper 8 bits of the keycode
swap d0
clr.w d0
move.b (kb_shift).w,d0
swap d0
lsl.l #8,d0
lsr.w #8,d0
move.l d0,d2 ; save keycode for our reset key commands
bclr #$1c,d2 ; clear capslock state
swap d2 ; isolate kb_shift & scancode
cmp.w #$c53,d2 ; control+alt+delete?
beq reseth ; => reset
cmp.w #$d53,d2 ; control+alt+rshift+delete?
beq _coldboot ; => cold boot by erasing all memory
btst #3,(conterm).w ; should Bconin() to return the shift status?
bne.s ari13b ; yes...
andi.l #$ffffff,d0 ; no, filter it out (default case)
ari13b: and.l #$ffff,d1
move.l d0,(a2,d1.l) ; store the data
move.w d1,8(a0) ; store the new buftail pointer
ari14: rts
keyclicksnd:move.l #gkeyclicksnd,(cursnd).w ; sound data for key click
move.b #0,(timer).w ; enable sound timer
rts
midibyte: movea.l (midivec).w,a2 ; get contents of midivec for indirect branch
jmp (a2) ; jump to midi interrupt handler
sysmidi: move.w 8(a0),d1 ; get current tail pointer offset
addq.w #1,d1 ; index = tail + 1
cmp.w 4(a0),d1 ; check to see if buffer should wrap
bcs.s mi13 ; no...
moveq #0,d1 ; wrap pointer
mi13: cmp.w 6(a0),d1 ; head=tail?
beq.s mi14 ; yes
movea.l (a0),a2 ; get buffer pointer
and.l #$ffff,d1
move.b d0,(a2,d1.l) ; store the data
move.w d1,8(a0) ; store the new buftail pointer
mi14: rts
keymaus1: moveq #5,d3 ; pre-init to "keyboard" right mouse button
btst #4,d1 ; see if it is a left or right button...
beq.s keym1 ; it's a right button ($47/$c7)
moveq #6,d3 ; it's a left button ($52/$d2)
keym1: btst #7,d1 ; see if it is a make or break action
beq.s keym2 ; it's a set button action (make code)
bclr d3,(kb_shift).w ; it's a clear button action (break code)
bra.s keym3 ; go to further pre-init action...
keym2: bset d3,(kb_shift).w ; it's a set button action (set code)
keym3: moveq #0,d1
moveq #0,d2
*
* finish up at the actual pseudo mouse routine
*
keymaus: lea (kmbuf).w,a0 ; point to key-emulating mouse buffer
movea.l (msintvec).w,a2 ; grab mouse interrupt vector
clr.l d0
move.b (kb_shift).w,d0 ; get current button status
lsr.b #5,d0 ; shift right button bit to 'd0'
addi.b #$f8,d0 ; add relative mouse header
move.b d0,(a0) ; store in first byte of record header
move.b d1,1(a0) ; store x value in second byte of record buffer
move.b d2,2(a0) ; store y value in third byte of record buffer
jsr (a2)
movea.l (sp)+,a0 ; restore kbufrec pointer
rts
mousekey1: DC.B $47,$c7,$52,$d2
*************************************************************************
* *
*************************************************************************
_coldboot: move #$2700,sr ; disable all IRQSR
IF ROM_STBOOK
move.l ($0004).w,(busexception).w ;a bus error triggers a reset
movea.w #addrexception,a0 ; start erasing from $c on
moveq #0,d0
move.l #$3fffc,d1 ; erase 1MB
_coldboot2: move.l d0,(a0)+
dbra d1,_coldboot2
movea.l ($0004).w,a0 ; jump into the ROM to reset the system
jmp (a0)
ELIF ROM_TOS206
move.w #5,d0
lea _coldbootr(pc),a0
movea.w #addrexception,a1
_coldboot1: move.l (a0)+,(a1)+ ; copy erase routine to $000c...
dbra d0,_coldboot1
jmp (addrexception).w
_coldbootr: move.l ($0004).w,(busexception).w
lea _coldbootre(pc),a0
moveq #0,d0
_coldbootl: move.l d0,(a0)+
move.l d0,(a0)+
move.l d0,(a0)+
move.l d0,(a0)+
bra.s _coldbootl
_coldbootre:nop
ELIF ROM_TOS306
move.l #$808,d0
movec d0,cacr
moveq #0,d0
movec d0,vbr ; set vector base to address 0
pmove ($e36738).l,tc ; 0L - disable PMMU
pmove ($e36738).l,tt0 ; 0L
pmove ($e36738).l,tt1 ; 0L
move.w #11,d0
lea _coldbootr(pc),a0
movea.w #addrexception,a1
_coldboot1: move.l (a0)+,(a1)+ ; copy erase routine to $000c...
dbra d0,_coldboot1
jmp (addrexception).w
_coldbootr: lea _coldboott(pc),a0
move.l a0,(busexception).w
lea _coldbootre(pc),a0
moveq #0,d0
_coldbootl: move.l d0,(a0)+ ; erase ST RAM
move.l d0,(a0)+
move.l d0,(a0)+
move.l d0,(a0)+
bra.s _coldbootl
_coldboott: move.l ($0004).w,(busexception).w
lea ($1000000).l,a0
_coldbootfl:move.l d0,(a0)+ ; erase fast RAM
move.l d0,(a0)+
move.l d0,(a0)+
move.l d0,(a0)+
bra.s _coldbootfl
_coldbootre:nop
ENDIF
*************************************************************************
* *
* protocol for accessing a gi sound chip register *
* *
* this bios call must be accessed in supervisor state *
* because it affects the 'sr' register *
* *
* entry *
* *
* word giaccess(data,register) *
* word data,register *
* *
* data -- data register read/write date *
* register -- chip register to select *
* d1 = #$0000 ;selects read operation of the register *
* d1 = #$80 .or .xx ;selects write xx to register *
* example write to portb - $80 .or. $0f = $8f *
* *
* exit *
* read operations *
* d0.b -- data register contains byte of date *
* write operations *
* d0.b -- data register contains a verification of written data *
* *
*************************************************************************
giaccess: move.w 4(sp),d0
move.w 6(sp),d1
gientry: move sr,-(sp)
ori #$700,sr
movem.l d1-d2/a0,-(sp) ; save affected registers
lea (psgsel).w,a0 ; init desired gi register addr
move.b d1,d2 ; make a copy to test for read or write
andi.b #$f,d1 ; turn off any extraneous bits
move.b d1,(a0) ; select register
asl.b #1,d2 ; shift once for carry bit detection
bcc.s giread ; carry clear, so do a read operation
giwrit: move.b d0,2(a0) ; init the memory location
giread: moveq #0,d0 ; clear our register
move.b (a0),d0 ; grab the data from the gi register
movem.l (sp)+,d1-d2/a0 ; restore affected registers
move (sp)+,sr
rts
*********************************************************************
* routine to turn on the dtr signal *
*********************************************************************
dtron: move.b #$ef,d2
bra.s offbit
*********************************************************************
* *
* routine to set any bit in the gi port a area *
* *
* entry *
* *
* word ongibit(bitnum) *
* word bitnum *
* *
* bitnum - byte size mask with desired bit set to "1" *
* *
*********************************************************************
ongibit: moveq #0,d2
move.w 4(sp),d2
onbit: movem.l d0-d2,-(sp)
move sr,-(sp)
ori #$700,sr
moveq #14,d1 ; get ready to read in the port a contents
move.l d2,-(sp)
bsr.s gientry ; go get it...
move.l (sp)+,d2
or.b d2,d0 ; set bit(s) on
move.b #$8e,d1 ; setup to write to port a
bsr.s gientry ; go set it and return
move (sp)+,sr
movem.l (sp)+,d0-d2
rts
*********************************************************************
* *
* routine to clear any bit in the gi port a area *
* *
* entry *
* *
* word offgibit(bitnum) *
* word bitnum *
* *
* bitnum - byte size mask with desired bit set to "0" *
* *
*********************************************************************
offgibit: moveq #0,d2
move.w 4(sp),d2
offbit: movem.l d0-d2,-(sp)
move sr,-(sp)
ori #$700,sr
moveq #14,d1 ; get ready to read in the port a contents
move.l d2,-(sp)
bsr.s gientry ; go get it...
move.l (sp)+,d2
and.b d2,d0 ; turn bit(s) off
move.b #$8e,d1 ; setup to write to port a
bsr.s gientry ; go set it and return
move (sp)+,sr
movem.l (sp)+,d0-d2
rts
*************************************************************************
* *
* EXTENDED RBP BIOS TIMER INIT CALL *
* *
* entry: *
* *
* word initmous(type,param,intvec) *
* word type *
* long param,initvec *
* *
* type - key/abs/rel/off mouse function requested *
* 4/ 2/ 1/ 0 value *
* param - address of parameter block *
* intvec - mouse interrupt vector *
* *
* *
* parameter block definition: *
* *
* byte 0 - y=0 at top/bottom; if non-zero then y=0 at bottom *
* otherwise y=0 at top *
* byte 1 - parameter for set mouse buttons command *
* byte 2 - x threshold/scale/delta parameter *
* byte 3 - y threshold/scale/delta parameter *
* *
* the following bytes are required for the absolute mode only *
* *
* byte 4 - xmsb for absolute mouse maximum position *
* byte 5 - xlsb for absolute mouse maximum position *
* byte 6 - ymsb for absolute mouse maximum position *
* byte 7 - ylsb for absolute mouse maximum position *
* byte 8 - xmsb for absolute mouse initial position *
* byte 9 - xlsb for absolute mouse initial position *
* byte a - ymsb for absolute mouse initial position *
* byte b - ylsb for absolute mouse initial position *
* *
*************************************************************************
* first we determine if the init is for a absolute, relative or keycode
* mouse action.
initmous: tst.w 4(sp) ; turn mouse off?
beq.s im1 ; yes...disable mouse
move.l $a(sp),(msintvec).w ; init the mouse interrupt vector
movea.l 6(sp),a3
cmpi.w #1,4(sp) ; relative mouse request?
beq.s im2 ; yes...
cmpi.w #2,4(sp) ; absolute mouse request?
beq.s im3 ; yes...
cmpi.w #4,4(sp) ; keycode mouse request?
beq.s im4 ; yes...
moveq #0,d0 ; error condition returned -- improper request
rts
im1: moveq #$12,d1 ; disable mouse
bsr ikbdput
move.l #xbtexit,(msintvec).w ; re-init the mouse interrupt vector
bra.s imexit
im2: lea (transbuf).w,a2 ; set transfer buffer pointer
move.b #8,(a2)+ ; set to relative mouse
move.b #$b,(a2)+ ; set relative mouse threshold x,y
bsr.s setmouse
moveq #6,d3 ; set length of the string -1 to transfer
lea (transbuf).w,a2 ; set transfer buffer pointer
bsr ikbdstr ; do transfer to ikbd
bra.s imexit
im3: lea (transbuf).w,a2 ; set transfer buffer pointer
move.b #9,(a2)+ ; set to absolute mouse
move.b 4(a3),(a2)+ ; set xmsb max
move.b 5(a3),(a2)+ ; set xlsb max
move.b 6(a3),(a2)+ ; set ymsb max
move.b 7(a3),(a2)+ ; set ylsb max
move.b #$c,(a2)+ ; set absolute mouse scale
bsr.s setmouse
move.b #$e,(a2)+ ; load initial absolute mouse position
move.b #0,(a2)+ ; filler load
move.b 8(a3),(a2)+ ; initial xmsb absolute mouse position
move.b 9(a3),(a2)+ ; initial xlsb absolute mouse position
move.b $a(a3),(a2)+ ; initial ymsb absolute mouse position
move.b $b(a3),(a2)+ ; initial ylsb absolute mouse position
moveq #$10,d3 ; set length of string -1 to transfer
lea (transbuf).w,a2 ; set transfer buffer pointer
bsr ikbdstr ; do transfer to ikbd
bra.s imexit
im4: lea (transbuf).w,a2 ; set transfer buffer pointer
move.b #$a,(a2)+ ; set to mouse keycode mode
bsr.s setmouse
moveq #5,d3 ; set length of string -1 to transfer
lea (transbuf).w,a2 ; set transfer buffer pointer
bsr ikbdstr ; do transfer to ikbd
imexit: moveq #-1,d0 ; set to true to indicate good init
rts
setmouse: move.b 2(a3),(a2)+ ; set x threshold/scale/delta
move.b 3(a3),(a2)+ ; set y threshold/scale/delta
moveq #$10,d1 ; setup to determine if top/bottom
sub.b (a3),d1 ; set y=0 at ?
move.b d1,(a2)+
move.b #7,(a2)+ ; set mouse button action
move.b 1(a3),(a2)+ ; mouse button parameter
rts
*************************************************************************
* *
* EXTENDED RBP BIOS TIMER INIT CALL *
* *
* entry: *
* *
* void xbtimer(id,control,data,intvec) *
* word id,control,data *
* long intvec *
* *
* intvec - timer interrupt vector *
* control - timer's control setting *
* data - timer's data register setting *
* id - timer id a-0, b-1, c-2, d-3 *
* *
* Special Note: *
* *
* In the interest of preserving as many features for the user *
* in the future, timer A should be reserved for the end-user *
* or independent software vendor's application program. System *
* software or those application needing just a "tick" should *
* constrain themselves to timer C, which is adequate for delay *
* and other timing uses. Future hardware may or may not bring *
* out the timer A input line out...giving software developers *
* another useful aspect of the machine to utilize. *
* *
* The recommended usage of the timers is as follows: *
* *
* Timer A - Reserved for end-users and stand-alone applications. *
* Timer B - Reserved for screen graphics, primarily. *
* Timer C - Reserved for system timing (GSX,GEM,DESKTOP,ET.AL). *
* Timer D - Reserved for baud rate control of RS-232 port, *
* the interrupt vector is available to anyone. *
* *
*************************************************************************
xbtimer: moveq #0,d0
moveq #0,d1
moveq #0,d2
move.w 4(sp),d0
move.w 6(sp),d1
move.w 8(sp),d2
bsr settimer ; setup the timer
tst.l $a(sp) ; if >$7fffffff then skip and exit
bmi.s xbtexit
movea.l $a(sp),a2 ; setup for initint call
moveq #0,d1 ; clear long
lea xbtim(pc),a1 ; point to timer -> interrupt # translation tab
andi.l #$ff,d0 ; mask off the highest three bytes in register
move.b (a1,d0.w),d0 ; setup for initint call
bsr initint
xbtexit: rts
xbtim: DC.B $0d,$08,$05,$04
*************************************************************************
* *
* KEYBOARD TRANSLATION TABLE CHANGE CALL *
* *
* entry: *
* *
* long keytrans(unshift,shift,capslock) *
* long unshift,shift,capslock *
* *
* -1 signifies no change to vector *
* *
* exit: *
* d0.l - returns pointer to beginning of *
* key translation address pointers *
* order of pointers is: *
* unshifted,shifted,caps-locked *
* Note: buffer space for each table should $80!! *
* *
*************************************************************************
keytrans: tst.l 4(sp)
bmi.s kt1
move.l 4(sp),(skeytran).w
kt1: tst.l 8(sp)
bmi.s kt2
move.l 8(sp),(skeyshif).w
kt2: tst.l $c(sp)
bmi.s kt3
move.l $c(sp),(skeycl).w
kt3: move.l #skeytran,d0
rts
*************************************************************************
* *
* RESTORE BIOS KEYBOARD TRANSLATION TABLE *
* *
* entry: *
* *
* void bioskeys() *
* *
* *
*************************************************************************
bioskeys: move.l #gskeytran,(skeytran).w
move.l #gskeyshif,(skeyshif).w
move.l #gskeycl,(skeycl).w
IF !ROM_TOS306
clr.b (keyrep).w
ENDIF
rts
*************************************************************************
* *
* RETURN IKBD SUBSYSTEM INTERRUPT TABLE POINTER *
* *
* entry: *
* *
* void dosound(ptr) *
* long ptr ;points to start of sound interpreter table *
* *
* *
*************************************************************************
dosound: move.l (cursnd).w,d0 ; return current status in D0.L
move.l 4(sp),d1 ; if new ptr < 0, then just return
bmi.s ds_r ; (invalid ptr, so return)
move.l d1,(cursnd).w ; setup new sound ptr
clr.b (timer).w ; zap sound timer register
ds_r: rts
*************************************************************************
* *
* SET/RETURN PRINTER CONFIGURATION WORD *
* *
* entry: *
* *
* word setptr(pconfig) *
* word pconfig ;sets/gets printer information word *
* *
* *
*************************************************************************
setprt: move.w (pconfig).w,d0 ; get current config word before we change it
tst.w 4(sp) ; see if we don't change the word
bmi.s nosetp ; don't set printer word
move.w 4(sp),(pconfig).w ; set printer config word
nosetp: rts
*************************************************************************
* *
* SET/RETURN KEY REPEAT VALUES *
* *
* entry: *
* *
* word kbrate(initial,repeat) *
* word initial,repeat *
* *
* initial determines the number of 50 hz cycles to wait before *
* a keyrepeat is to commence. repeat determines the interval *
* between keyrepeats after the initial pause. *
* *
*************************************************************************
kbrate: move.w (cdelay1).w,d0 ; get current initial/repeat values
tst.w 4(sp) ; see if we don't change the word
bmi.s kbrate1 ; don't set key repeat values
move.w 4(sp),d1 ; set key repeat values
move.b d1,(cdelay1).w ; set initial delay
tst.w 6(sp) ; see if we don't change the word
bmi.s kbrate1 ; don't set key repeat values
move.w 6(sp),d1 ; set key repeat values
move.b d1,(cdelay2).w ; set subsequent delay
kbrate1: rts
*************************************************************************
* *
* RETURN POINTER TO IKBD/MIDI INTERRUPT VECTORS *
* *
* entry: *
* *
* long ikbdvecs() *
* returns a pointer to the midi interrupt vector and *
* ikbd subsystem interrupt vector table. the table *
* structure is as follows: *
* *
* midivec ds.l 1 ;midi interrupt handler vector *
* vkbderr ds.l 1 ;keyboard error handler address *
* vmiderr ds.l 1 ;midi error handler address *
* statintvec ds.l 1 ;ikbd status interrupt vector *
* msintvec ds.l 1 ;mouse interrupt vector *
* clkintvec ds.l 1 ;realtime clk interrupt vector *
* joyintvec ds.l 1 ;joystick interrupt vector *
* *
* note: msintvec is modified via the initmouse system function *
* call. since gem uses this vector, modifying it can be *
* fatal while running under gem. clkintvec is used by *
* gemdos. its pre-inited vector must be restored for *
* proper gemdos operation. Caveat hacker! *
* *
* *
*************************************************************************
ikbdvecs: move.l #midivec,d0
rts
*************************************************************************
* *
* C Timer interrupt routine to process the PSG sound table *
* *
*************************************************************************
*+ (lmd)
* timercint - timer c interrupt handler
* divide 200 Hz interrupt frequency to 50 hz, and do:
* sound handler processing
* key-repeat processing;
* control-g bell and keyclick if enabled via sound handler
* system timer-tick handoff
* updates: tc_rot (every tick)
*
* imports: etv_timer (timer handoff vector)
* _timr_ms (timer calibration value)
*
*-
timercint: addq.l #1,(_hz_200).l ; increment raw tick counter
rol (tc_rot).l ; rotate divisor bits
bpl.s t_punt ; if not 4th interrupt, then return
movem.l d0-d7/a0-a6,-(sp)
bsr.s sndirq ; process sounds
btst #1,(conterm).w ; check for key repeat enabled
beq.s krexit ; not enabled
* process for repeat key function first because it can affect the sound
* table if enabled and the user is 'using'...
tst.b (keyrep).w
beq.s krexit
tst.b (keydelay1).w
beq.s kr1
subq.b #1,(keydelay1).w
bne.s krexit
kr1: subq.b #1,(keydelay2).w
bne.s krexit
move.b (cdelay2).w,(keydelay2).w
move.b (keyrep).w,d0
lea (kbufrec).w,a0
bsr ari16 ; repeat key stroke and stuff into buffer
*+ (lmd)
* Call system timer vector
* (first guy in the system daisy-chain)
*
*-
krexit: move.w (_timr_ms).w,-(sp) ; push #ms/tick
movea.l (etv_timer).w,a0 ; get vector
jsr (a0) ; call it
addq.w #2,sp ; cleanup stack
tick1: movem.l (sp)+,d0-d7/a0-a6
t_punt: move.b #$df,(isrb).w ; clear the interrupt channel
rte
*********************************************************
*
* Quick & dirty sound stuff
*
*
* Programmed by Dave Staugas
* 14 Mar 1985
*
*
*
*
*********************************************************
*
*
*
*
* To start a sound, load the 32-bit address of the
* byte stream for that sound in 32-bit
* "cursnd", & zero the 8-bit "timer"
*
*
*
*
* Sound interrupt routine
* Called from timer C irq
*
sndirq: movem.l d0-d1/a0,-(sp)
move.l (cursnd).w,d0 ; get current sound ptr
beq.s snd1 ; br to exit if zero, inactive
movea.l d0,a0 ; ptr to a0
move.b (timer).w,d0 ; check delay timer
beq.s snd3 ; br over delay timer update if not on
*
subq.b #1,d0 ; tick off delay timer
move.b d0,(timer).w ; save new
bra.s snd1 ; skip sound update this time
snd3: move.b (a0)+,d0 ; pick up next sound command
bmi.s snd2 ; if minus, go do special
*
move.b d0,(psgsel).w ; else, register load command--select this
cmpi.b #7,d0 ; reg. 7 selected?
bne.s sn1 ; br if no
*
move.b (a0)+,d1 ; get data to write to reg 7
andi.b #$3f,d1 ; always leave i/o port settings alone
move.b (psgsel).w,d0 ; get mixer contents
andi.b #$c0,d0 ; mask off non-useful info...
or.b d1,d0 ; generate new setting
move.b d0,(psgwr).w ; write data
bra.s snd3 ; go for next command
sn1: move.b (a0)+,(psgwr).w ; write next byte as data directly to reg
bra.s snd3 ; go for next command
*
* special case command
*
snd2: addq.b #1,d0 ; was command 255?
bpl.s snd5 ; br if yes--set delay timer
*
cmpi.b #$81,d0 ; was command 128 (before increment)
bne.s snd6 ; br if not
*
* command 128
*
move.b (a0)+,(auxd).w ; 128--set aux data from next byte in stream
bra.s snd3 ; go for next command
*
* command > 128
*
snd6: cmpi.b #$82,d0 ; command greater than 129
bne.s snd5 ; br if yes--must be set timer
*
* command 129
*
move.b (a0)+,(psgsel).w ; 129--select register
move.b (a0)+,d0 ; get increment step (signed)
add.b d0,(auxd).w ; add to aux data
move.b (a0)+,d0 ; terminating value
move.b (auxd).w,(psgwr).w ; load reg from data in auxd
cmp.b (auxd).w,d0 ; reached end of cycle?
beq.s snd4 ; br if so
*
* still within loop, reset sound pointer to iterate for next irq
*
subq.w #4,a0 ; back up sound ptr to repeat this command
bra.s snd4 ; update ptr & exit
*
* set delay timer
*
snd5: move.b (a0)+,(timer).w ; set delay timer from next byter in stream
bne.s snd4 ; if non-zero real delay here
movea.w #0,a0 ; else, sound terminator--set ptr to null
snd4: move.l a0,(cursnd).w ; update sound ptr
snd1: movem.l (sp)+,d0-d1/a0 ; pop stack & exit
rts
*
* sound data...
*
*
* format:
*
* sound data usually is found in byte pairs, the first of which is the command
* and the second is the argument. However, some commands take on more than
* 1 argument
*
* cmd function argument(s)
* 00 load reg0 data0
* 01 load reg1 data0
* 02 load reg2 data0
* 03 load reg3 data0
* 04 load reg4 data0
* 05 load reg5 data0
* 06 load reg6 data0
* 07 load reg7 data0 note: b7 & b6 forced set for all data to r...
* 08 load reg8 data0
* 09 load reg9 data0
* 0A load reg10 data0
* 0B load reg11 data0
* 0C load reg12 data0
* 0D load reg13 data0
*
*
* 80 init temp w/ data0
*
* 81 loop defined
* by 3 args data0 as register to load using temp
* data1 as increment/decrement (signed) of temp
* data2 as loop terminator value of temp
*
* 82-FF set delay
* timer data0 is # of counts till next update
* note: if data0 = 0, sound is terminated
*
*
*
*
*********************************************************
* VT52 emulator calls for the bell sound
*********************************************************
ringbel: btst #2,(conterm).w ; console bell enabled?
beq.s rgbel ; (no sound)
movea.l (bell_hook).l,a0
jsr (a0) ; go through the bell vector
rts
keybellsnd: move.l #bellsnd,(cursnd).w ; sound data for console bell
move.b #0,(timer).w ; enable sound timer
rgbel: rts
*+
* flopini - initialize floppies
* Passed (on the stack):
* $c(sp) devno
* $8(sp) ->DSB
* $4(sp) ->buffer (unused)
* $0(sp) return address
*
* Returns: EQ if initialization succeeded (drive attached).
* NE if initialization failed (no drive attached).
*-
_flopini: clr.l (frbufcookie).l
lea (dsb0).l,a1 ; get ptr to correct DSB
tst.w $c(sp)
beq.s fi_1
lea (dsb1).l,a1
fi_1: move.w (seekrate).l,6(a1) ; setup default seek rate
move.w #3,4(a1) ; default: HD mode
moveq #-1,d0 ; (default error)
clr.w 2(a1) ; fake clean drive
clr.w (frbufscnt).l
IF !ROM_TOS306
move.w #$ff00,2(a1) ; default = recal drive (it's dirty)
ENDIF
IF ROM_STBOOK
bsr.s checkfdc
beq.s fi_ndrv
ENDIF
bsr floplock ; setup parameters
bsr select ; select drive an side
IF ROM_TOS306
move.w #$ff00,2(a1) ; default = recal drive (it's dirty)
ENDIF
bsr restore ; attempt restore
beq.s fi_ok ; (quick exit if that won)
moveq #10,d7 ; attempt seek to track 10
bsr hseek1 ; (hard seek to 'd7')
bne.s fi_nok ; (failed: drive unusable)
bsr restore ; attempt restore after seek
fi_ok: beq flopok ; return OK (on win)
fi_nok: bra flopfail ; return failure
IF ROM_STBOOK
fi_ndrv: move.w #$0202,(diskmode).w ; both drives are "unsure"
moveq #0,d0
tst.w (_nflops).w ; any floppy drives installed?
beq.s fi_ret ; (no, return NO_ERROR)
moveq #-15,d0 ; return "unknown device"
fi_ret: rts
drvnrdy: moveq #-2,d0 ; return DRIVE_NOT_READY
rts
ENDIF
IF ROM_TOS306
move.l a0,(busexception).w
movea.l a1,sp
movem.l (sp)+,d7/a6
drvnrdy: moveq #-15,d0
rts
ENDIF
*+
* checkfdc - check if the FDC track/sector register are available
* Returns: EQ, if the FDC is not available
* NE, if the FDC is available
*-
IF ROM_STBOOK
checkfdc: addq.w #1,(flock).w ; lock floppies (allow 'recursion')
movem.l d0-d7/a6,-(sp)
lea (fifo).w,a6
move.w #$82,(a6) ; setup 1770 track register
moveq #0,d7
bsr wdiskctl
move.w #$84,(a6) ; load 1770 sector register
moveq #-1,d7
bsr wdiskctl
move.w #$82,(a6) ; read 1770 track register
bsr rdiskctl
move.b d0,d1 ; not equal to 0?
bne.s checkfdc2
move.w #$84,(a6) ; read 1770 sector register
bsr rdiskctl
subq.w #1,(flock).w ; unlock floppies
tst.b d1 ; track register wrong?
bne.s checkfdc2
cmp.b #$ff,d0 ; sector register wrong?
checkfdc2: movem.l (sp)+,d0-d7/a6
eori #4,sr ; flip Z bit (return NE if bit is zero)
rts
ENDIF
*+
* floprd - read sector from floppy
* Passed (on the stack):
* $14(sp) count
* $12(sp) sideno
* $10(sp) trackno
* $e(sp) sectno
* $c(sp) devno
* $8(sp) ->DSB
* $4(sp) ->buffer
* $0(sp) return address
*
* Returns EQ, the read won (on all sectors),
* NE, the read failed (on some sector).
*-
_floprd:
IF ROM_STBOOK
bsr.s checkfdc
beq.s drvnrdy
ENDIF
IF ROM_TOS306
tst.w (_nflops).w ; any floppy drives installed?
beq.s drvnrdy ; (no, return "unknown device")
ENDIF
bsr change ; test for disk change
moveq #-11,d0 ; set default error#
bsr frbcheckrd
bsr floplock ; lock floppies, setup parameters
frd1: bsr select ; select drive, setup registers
bsr go2track ; seek appropriate track
bcs flopfail
bne frde ; retry on seek failure
frd1l: move.w #-1,(curr_err).l ; set general error#
move.w #$190,(a6) ; toggle DMA data direction,
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #$90,(a6) ; leave hardware in READ state
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #1,(diskctl).w ; set sector count register
move.w #$80,(a6) ; startup 1770 "read sector" command
move.w #$80,d7 ; (read single)
bsr wdiskctl
move.l (_hz_200).w,d7
add.l #300,d7 ; set timeout timer
*--- Wait for read completion:
frd2: btst #5,(gpip).w ; 1770 done yet?
beq.s frd4 ; (yes)
cmp.l (_hz_200).w,d7 ; timeout reached?
bhi.s frd2 ; (punt on timeout)
*---- check status after read
move.w #-2,(curr_err).w ; set "timeout" error
bsr reset1770 ; (clobber 1770)
bra.s frde ; (go retry)
*--- check status after read:
frd4: move.w #$90,(a6) ; examine DMA status register
move.w (a6),d0
btst #0,d0 ; bit zero inidcates DMA ERROR
beq.s frde ; (when its zero -- retry)
move.w #$80,(a6) ; exeamine 1770 status register
bsr rdiskctl
and.b #$1c,d0 ; check for RNF, checksum, lost-data
bne.s frd4 ; (bail on error)
move.w #2,(retrycnt).w ; reset retry count for next sector
addq.w #1,(csect).w ; advance sector #
addi.l #$200,(cdma).w ; advance buffer by 512 bytes
subq.w #1,(ccount).w ; decrement sector count
beq flopok ; (done)
bsr select1
bra frd1l
frd4: bsr.s err_bits ; set error# from 1770 bits
frde: cmpi.w #1,(retrycnt).w ; are we on the "middlemost" retry?
bne.s frd5
frde1: bsr reseek ; yes, home and reseek the head
frd5: subq.w #1,(retrycnt).w ; drop retry count
bpl frd1 ; (continue of any retries left)
bra flopfail ; fail when we run out of patience
*+
* err_bits - set "curr_err" according to 1770 error status
* Passed: d0 = 1770 status
*
* Returns: curr_err, containing current error number
*
* Uses: d1
*-
err_bits: moveq #-13,d1 ; write protect?
btst #6,d0
bne.s eb1
moveq #-8,d1 ; record-not-found?
btst #4,d0
bne.s eb1
moveq #-4,d1 ; CRC error?
btst #3,d0
bne.s eb1
move.w (def_error).w,d1 ; use default error#
eb1: move.w d1,(curr_err).w ; set current error number & return
rts
*+
* flopwr - write sector to floppy
* Passed (on the stack):
* $14(sp) count
* $12(sp) sideno
* $10(sp) trackno
* $e(sp) sectno
* $c(sp) devno
* $8(sp) ->DSB
* $4(sp) ->buffer (unused)
* $0(sp) return address
*
* Returns: EQ, the write won (on all sectors),
* NE, the write failed (on some sectors).
*-
_flopwr:
IF ROM_STBOOK
bsr checkfdc
beq drvnrdy
ENDIF
IF ROM_TOS306
tst.w (_nflops).w ; any floppy drives installed?
beq drvnrdy ; (no, return "unknown device")
ENDIF
bsr change ; check for disk swap
moveq #-10,d0 ; set default error number
bsr frbcheckwr
bsr floplock ; lock floppies
*+
* If the boot sector is written to,
* set the media change mode to "unsure".
* (Kludge, kludge, kludge....)
*-
move.w (csect).w,d0 ; sector 1
subq.w #1,d0
or.w (ctrack).w,d0 ; track 0
or.w (cside).w,d0 ; side 0
bne.s fwr1 ; if not boot sector, then OK
moveq #2,d0 ; set media change mode to unsure
bsr setdmode ; (boy, is this /ugly/)
fwr1: bsr select ; select drive
bsr go2track ; seek
bcs flopfail
bne fwre1 ; (retry on seek failure)
fwr1a: move.w #-1,(curr_err).w ; set general error#
move.w #$90,(a6) ; toggle DMA chip to clear status
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #$190,(a6) ; leave in WRITE mode
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #1,d7 ; load sector-count register
bsr wdiskctl
move.w #$180,(a6) ; load "WRITE SECTOR" command
move.w #$a0,d7 ; into 1770 cmdreg
bsr wdiskctl
move.l (_hz_200).w,d7
add.l #300,d7 ; d7 = timeout timer
fwr2: btst #5,(gpip).w ; done yet?
beq.s fwr4 ; (yes, check status)
cmp.l (_hz_200).w,d7 ; timeout reached?
bhi.s fwr2 ; (still tickin')
bsr reset1770 ; timed out -- reset 1770
bra.s fwre ; and retry
fwr4: move.w #$180,(a6) ; get 1770 status
bsr rdiskctl
bsr err_bits ; compute 1770 error bits
btst #6,d0 ; if write protected, don't retry
bne flopfail ; (can't write, so punt)
and.b #$5c,d0 ; check WriteProt+RecNtFnd+CHKSUM+LostD
bne.s fwre ; retry on error
move.w #2,(retrycnt).w ; reset retry count
addq.w #1,(csect).w ; bump sector number
addi.l #$200,(cdma).w ; add DMA pointer for next sector
subq.w #1,(ccount).w ; if(!--count) return OK;
beq flopok
bsr select1 ; setup sector#, DMA pointer
bra fwr1a ; write next (no seek)
fwre: cmpi.w #1,(retrycnt).w ; re-seek head in "middle" retry
bne.s fwr5 ; (not middle retry)
fwre1: bsr reseek ; home head and seek
fwr5: subq.w #1,(retrycnt).w ; decrement retry count
bpl fwr1 ; loop if there's still hope
bra flopfail ; otherwise return error status
*+
* _flopfmt - format a track
* Passed (on the stack):
* $1a(sp) initial sector data
* $16(sp) magic number
* $14(sp) interleave
* $12(sp) side
* $10(sp) track
* $e(sp) spt
* $c(sp) devno
* $8(sp) pointer to state block
* $4(sp) dma address
* $0(sp) [return]
*
* Returns: EQ: track successfully written. Zero.W-terminated list of
* bad sectors left in buffer (they might /all/ be bad.)
*
* NE: could not write track (write-protected, drive failure,
* or something catastrophic happened).
*-
_flopfmt: cmpi.l #$87654321,$16(sp) ; check for magic# on stack
bne flopfail ; no magic, so we just saved the world
IF ROM_STBOOK
bsr checkfdc
beq drvnrdy
ENDIF
IF ROM_TOS306
tst.w (_nflops).w ; any floppy drives installed?
beq drvnrdy ; (no, return "unknown device")
ENDIF
bsr change ; check for disk flip
moveq #-10,d0 ; set default error number
bsr frbcheckfmt
bsr floplock
bsr select ; select drive and side
move.w $e(sp),(spt).w ; save sectors-per-track
move.w $14(sp),(interlv).w ; save interleave factor
move.w $1a(sp),(virgin).w ; save initial sector data
move.l 8(sp),(fmtstateblk).w
*--- put drive into "changed" mode
moveq #2,d0 ; d0 = "CHANGED"
bsr setdmode ; set media change mode
moveq #3,d0 ; HD mode
cmpi.w #$d,(spt).w ; 13 sectors or more require HD mode
bcc.s flopfmt1
moveq #0,d0 ; SD mode
flopfmt1:
IF ROM_STBOOK
bsr hdselect ; select SD/HD mode of floppy
ELSE
IF ROM_TOS206
tst.b (STEFlag).l ; no STE hardware available?
bne.s flopfmt2 ; (correct)
ENDIF
move.w d0,(fdccs).w
flopfmt2:
ENDIF
move.w d0,4(a1)
*--- seek to track (hard seek):
bsr hseek ; hard seek to 'ctrack'
bne flopfail ; (return error on seek failure)
move.w (ctrack).w,2(a1) ; record current track#
*--- format track, then verify it:
move.w #-1,(curr_err).w ; vanilla error mode
bsr.s fmtrack ; format track
bne flopfail ; (return error on seek failure)
move.w (spt).w,(ccount).w ; set number of sectors to verify
move.w #1,(csect).w ; starting sector# = 1
bsr verify1 ; verify sectors
*--- if there are any bad sectors, return /that/ error...
movea.l (cdma).w,a2 ; a2 -> bad sector list
tst.w (a2) ; any bad sectors?
beq flopok ; no -- return OK
move.w #-16,(curr_err).w ; set error number
bra flopfail ; return error
*+
* fmtrack - format a track
* Passed: variables setup by _flopfmt
* Returns: NE on failure, EQ on success
* Uses: almost everything
* Called-by: _flopfmt
*
*-
fmtrack: move.w #-10,(def_error).w ; set default error number
movea.l (cdma).w,a2 ; a2 -> prototyping area
movea.l (fmtstateblk).w,a3
moveq #120-1,d1 ; 120 x $4e (track leadin)
cmpi.w #$d,(spt).w
bcc.s fmtrack1
moveq #60-1,d1 ; 60 x $4e (track leadin)
fmtrack1: moveq #$4e,d0
bsr wmult
clr.w d3 ; interleave index = table start
tst.w (interlv).w ; interleave < 0
bmi fmtrack2 ; use custom interleave table ->
moveq #1,d3 ; first sector = 1
*---- address mark
ot3: move.w d3,d4 ; d4 = starting sector (this pass)
ot1: moveq #12-1,d1 ; 12 x $00
clr.b d0
bsr wmult
moveq #3-1,d1 ; 3 x $f5
moveq #$f5,d0
bsr wmult
move.b #$fe,(a2)+ ; $fe -- address mark intro
move.b (ctrack+1).w,(a2)+ ; track#
move.b (cside+1).w,(a2)+ ; side#
move.b d4,(a2)+ ; sector#
move.b #2,(a2)+ ; sector size (512)
move.b #$f7,(a2)+ ; write checksum
*--- gap between AM and data:
moveq #22-1,d1 ; 22 x $4e
moveq #$4e,d0
bsr wmult
moveq #12-1,d1 ; 12 x $00
clr.b d0
bsr wmult
moveq #3-1,d1 ; 3 x $f5
moveq #$f5,d0
bsr wmult
*--- data block:
move.b #$fb,(a2)+ ; $fb -- data intro
move.w #256-1,d1 ; 256 x virgin.W (initial sector data)
ot2: move.b (virgin).w,(a2)+ ; copy high byte
move.b (virgin+1).w,(a2)+ ; copy low byte
dbra d1,ot2 ; fill 512 bytes
move.b #$f7,(a2)+ ; $f7 -- write checksum
moveq #40-1,d1 ; 40 x $4e
moveq #$4e,d0
bsr wmult
tst.w (interlv).w ; interleave < 0
bmi fmtrack2 ; use custom interleave table ->
add.w (interlv).w,d4 ; bump sector#
cmp.w (spt).w,d4 ; if(d4 <= spt) then_continue;
ble.s ot1 ; proto more sectors this pass
addq.w #1,d3 ; bump pass start count
cmp.w (interlv).w,d3 ; if(d3 <= interlv) then_continue;
ble.s ot3
*--- end-of-track
ot5: move.w #2800,d1 ; 2801 x $4e -- end of track trailer
cmpi.w #$d,(spt).w
bcc.s ot4
move.w #1400,d1 ; 1401 x $4e -- end of track trailer
ot4: moveq #$4e,d0
bsr wmult
*--- setup to write the track:
move.b (cdma+3).w,(dmalow).w ; load dma pointer
move.b (cdma+2).w,(dmamid).w
move.b (cdma+1).w,(dmahigh).w
move.w #$90,(a6) ; toggle R/W flag and
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #$190,(a6) ; select sector-count register
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
moveq #$60,d7 ; (absurd sector count)
bsr wdiskctl
move.w #$180,(a6) ; select 1770 cmd register
move.w #$f0,d7 ; write format_track command
bsr wdiskctl
move.l (_hz_200).w,d7
add.l #300,d7 ; d7 = timeout timer
*--- wait for 1770 complete:
otw1: btst #5,(gpip).w ; is 1770 done?
beq.s otw2 ; (yes)
cmp.l (_hz_200).w,d7 ; timeout reached?
bhi.s otw1 ; (still tickin')
bsr reset1770 ; timed out -- reset 1770
oterr: moveq #1,d7 ; return NE (error status)
rts
fmtrack2: cmp.w (spt).w,d3 ; last sector reached?
beq ot5 ; yes -> end of track
move.w d3,d6
add.w d6,d6
move.w (a3,d6.w),d4 ; pick new sector number from the table
addq.w #1,d3 ; increment interleave index
bra ot1
*--- see if the write-track won:
otw2: move.w #$190,(a6) ; check DMA status bit
move.w (a6),d0
btst #0,d0 ; if its zero, there was a DMA error
beq.s oterr ; (so return NE)
move.w #$180,(a6) ; get 1770 status
bsr rdiskctl
bsr err_bits ; set 1770 error bits
and.b #$44,d0 ; check for writeProtect & lostData
rts ; return NE on 1770 error
*----- write 'D1+1' copies of D0.B into A2, A2+1, ...
wmult: move.b d0,(a2)+ ; record byte in proto buffer
dbra d1,wmult ; (do it again)
rts
*+
* _flopver - verify sectors on a track
* $14(sp) count
* $12(sp) sideno
* $10(sp) trackno
* $e(sp) sectno
* $c(sp) devno
* $8(sp) ->DSB
* $4(sp) ->buffer (at least 1K long)
* $0(sp) return address
*
* Returns: NULL.W-terminated list of bad sectors in the buffer if D0 == 0,
* OR some kind of error (D0 < 0).
*
*-
_flopver:
IF ROM_STBOOK
bsr checkfdc
beq drvnrdy
ENDIF
IF ROM_TOS306
tst.w (_nflops).w ; any floppy drives installed?
beq drvnrdy ; (no, return "unknown device")
ENDIF
bsr change ; hack disk change
moveq #-11,d0 ; set default error#
bsr frbcheckfmt
bsr floplock ; lock floppies, setup parameters
bsr select ; select floppy
bsr go2track ; go to track
bne flopfail ; (punt if that fails)
bsr.s verify1 ; verify some sectors
bra flopok ; return "OK"
*+
* verify1 - verify sectors on a single track
* Passed: csect = starting sector#
* ccount = number of sectors to verify
* cdma -> 1K buffer (at least)
*
* Returns: NULL.W-terminated list of bad sectors (in the buffer)
* (buffer+$200..buffer+$3ff used as DMA buffer)
*
* Enviroment: Head seeked to correct track;
* Drive and side already selected;
* Motor should be spinning (go2track and fmttrack do this).
*
* Uses: Almost everything.
*
* Called-by: _flopfmt, _flopver
*
*-
verify1: move.w #-11,(def_error).w ; set default error number
movea.l (cdma).w,a2 ; a2 -> start of bad sector list
addi.l #$200,(cdma).w ; bump buffer up 512 bytes
*--- setup for (next) sector
tvrlp: move.w #2,(retrycnt).w ; init sector-retry count
move.w #$84,(a6) ; load 1770 sector register
move.w (csect).w,d7 ; with 'csect'
bsr wdiskctl
*--- setup for sector read
tvr1: move.b (cdma+3).w,(dmalow).w ; load dma pointer
move.b (cdma+2).w,(dmamid).w
move.b (cdma+1).w,(dmahigh).w
move.w #$190,(a6) ; toggle R/W (leave in W state)
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #$90,(a6)
tst.b (gpip).w
tst.b (gpip).w ; delay for 1 microsec
tst.b (gpip).w ; this amounts to 16 16Mhz clocks
tst.b (gpip).w
move.w #1,d7 ; set DMA sector count to 1
bsr wdiskctl
move.w #$80,(a6) ; load 1770 command register
move.w #$80,d7 ; with ReadSector command
bsr wdiskctl
move.l (_hz_200).w,d7
add.l #300,d7 ; d7 = timeout timer
*--- wait for command completion
tvr2: btst #5,(gpip).w ; test for 1770 done
beq.s tvr4 ; (yes, it completed)
cmp.l (_hz_200).w,d7 ; timeout reached?
bhi.s tvr2 ; (still tickin')
bsr reset1770 ; reset controller and return error
bra.s tvre
*--- got "done" interrupt, check DMA status:
tvr4: move.w #$90,(a6) ; read DMA error status
move.w (a6),d0
btst #0,d0 ; if DMA_ERROR is zero, then retry
beq.s tvre
*--- check 1770 completion status (see if it's happy):
move.w #$80,(a6) ; read 1770 status register
bsr rdiskctl
bsr err_bits ; set error# from 1770 register
and.b #$1c,d0 ; check for record-not-found, crc-error,
bne.s tvre ; and lost data; return on error
*--- read next sector (or return if done)
tvr6: addq.w #1,(csect).w ; bump sector count
subq.w #1,(ccount).w ; while(--count) read_another;
bne tvrlp
subi.l #$200,(cdma).w ; readjust DMA pointer
clr.w (a2) ; terminate bad sector list
rts ; and return EQ
*--- read failure: retry or record bad sector
tvre: cmpi.w #1,(retrycnt).w ; re-seek head?
bne.s tvr5 ; (no)
bsr reseek ; yes: back to home and then back
tvr5: subq.w #1,(retrycnt).w ; to the current track...
bpl tvr1
move.w (csect).w,(a2)+ ; record bad sector
bra.s tvr6 ; do next sector
*+
* _flopvbl - floppy vblank handler
* Deselects floppies after the motor stops
*-
_flopvbl:
IF ROM_TOS306
tst.w (_nflops).w ; any floppy drives installed?
beq fvblr ; (no, return "unknown device")
ENDIF
lea (fifo).w,a6 ; a6 -> fifo
st (_motoron).w ; assume motor is on
tst.w (flock).w ; floppies locked?
bne.s fvblr ; (yes, so don't touch them)
IF ROM_STBOOK
bsr checkfdc
beq.s fvblr
ENDIF
*--- write-protect monitor:
move.l (_frclock).w,d0 ; check a drive every 8 jiffies
move.b d0,d1 ; (save jiffy count)
and.b #7,d1 ; time yet?
bne.s fvbl1 ; (no)
move.w #$80,(a6) ; select 1770 command/status register
*--- select drive, record it's WP status:
lsr.b #3,d0 ; use bit 4 as drive# to check
and.w #1,d0
lea (wpstatus).w,a0
adda.w d0,a0
cmp.w (_nflops).w,d0
bne.s fvbl2
clr.w d0
fvbl2: addq.b #1,d0
lsl.b #1,d0 ; (magic shift left)
eori.b #7,d0 ; invert select bits, select side 0
bsr setporta ; set port A (d2 = old bits)
bsr rdiskctl ; get 1770 status
move.w d0,d1
btst #6,d1 ; test Write-Protect status bit
sne (a0) ; set WP status to $00 or $FF.
move.b d2,d0 ; restore old drive-select bits
bsr setporta
fvbl1: move.w (wpstatus).w,d0 ; or _wpstatus into _wplatch
or.w d0,(wplatch).w ; (catch any WP transitions)
*--- floppy deselect test
tst.w (deselflg).w ; floppies already deselected?
bne.s fvblr1 ; (yes, so don't do it again)
move.l (_hz_200).w,d0
cmp.l (fltimeout).l,d0 ; did we hit our 5s timeout?
bcc.s fvbl2
bsr rdiskctl ; read 1770 status register
btst #7,d0 ; is the motor still on?
bne.s fvblr ; (yes, so don't deselect)
fvbl2: move.b #7,d0 ; deselect both drives
bsr setporta ; (set bits 0..3 in portA of PSG)
move.w #1,(deselflg).w ; indicate floppies deselected
fvblr1: clr.w (_motoron).w ; indicate motor is OFF
fvblr: rts ; back to vbl
*+
* floplock - lock floppies and setup floppy parameters
*
* Passed (on the stack):
* $18(sp) - count.W (sector count)
* $16(sp) - side.W (side#)
* $14(sp) - track.W (track#)
* $12(sp) - sect.W (sector#)
* $10(sp) - dev.W (device#)
* $c(sp) - obsolte.L
* 8(sp) - dma.L (dma pointer)
* 4(sp) - ret1.L (caller's return address)
* 0(sp) - ret.L (floplock's return address)
*
* Passed: D0.W = default error number
*-
floplock: movem.l d3-d7/a3-a6,($168a).l ; save C registers
lea (fifo).w,a6 ; a6 -> fifo
st (_motoron).w ; kludge motor state = ON
move.w d0,(def_error).w ; set default error number
move.w d0,(curr_err).w ; set current error number
move.w #1,(flock).w ; tell vbl not to touch floppies
move.l 8(sp),(cdma).w ; cdma -> /even/ DMA address
move.w $10(sp),(cdev).w ; save devide# (0 . 1)
move.w $12(sp),(csect).w ; save sector# (1 . 9, usually)
move.w $14(sp),(ctrack).w ; save track# (0 . 39 . 79 )
move.w $16(sp),(cside).w ; save side# (0 . 1)
move.w $18(sp),(ccount).w ; save sector count (1..spt)
move.w #2,(retrycnt).w ; setup retry count
*--- pick a DSB
lea (dsb0).w,a1
tst.w (cdev).w
beq.s flock2
lea (dsb1).w,a1
*--- recalibrate drive (if it needs it)
flock2: tst.w 2(a1) ; if (curtrack < 0) recalibrate()
bpl.s flockr
bsr select ; select drive & side
clr.w 2(a1) ; we're optimistic -- assume winnage
bsr restore ; attempt restore
beq.s flockr ; (it won)
moveq #10,d7 ; attempt seek to track 10
bsr hseek1
bne.s flock1 ; (failed)
bsr restore ; attempt restore again
beq.s flockr ; (it won)
flock1: move.w #$ff00,2(a1) ; complete failure (what can we do?)
flockr: rts
*+
* flopfail - unlock floppies and return error.
*
*-
flopfail: moveq #1,d0 ; disk change mode = unsure
bsr setdmode ; set media change mode
move.w (curr_err).w,d0 ; get current error number
ext.l d0 ; extent to long
bra.s unlok1 ; clobber floppy lock & return
*+
* flopok - unlock floppies and return success status:
*
*-
flopok: clr.l d0 ; return 0 (success)
unlok1: move.l d0,-(sp) ; (save return value)
IF ROM_TOS306
bsr flushCaches
ENDIF
move.w #$86,(a6) ; force WP to real-time mode
move.w 2(a1),d7 ; dest-track = current track
bsr wdiskctl
move.w #$10,d6 ; cmd = seek w/o verify
bsr flopcmds ; do it
move.l (_hz_200).l,d0
add.l #5*200,d0 ; 5s floppy deselect timeout
move.l d0,(fltimeout).l
move.w (cdev).w,d0 ; set last-access time for 'cdev'
lsl.w #2,d0
lea (_acctim).w,a0
move.l (_frclock).w,(a0,d0.w)
cmpi.w #1,(_nflops).w ; if (nflops == 1) set other time, too
bne.s unlok2
move.l (_frclock).w,4(a0) ; set last-access time for floppy 1
unlok2: move.l (sp)+,d0 ; restore return value
movem.l ($168a).w,d3-d7/a3-a6 ; restore C registers
clr.w (flock).l ; unlock floppies
bsr frbbackcopy ; if necessary copy FRB buffer back
rts
*+
* hseek - seek to 'ctrack' without verify
* hseek1 - seek to 'd7' without verify
* hseek2 - seek to 'd7' without verify, keep current error number
*
* Returns: NE on seek failure ("cannot happen"?)
* EQ if seek wins
*
* Uses: d7, d6, ...
* Jumps-to: flopcmds
* Called-by: _flopfmt, _flopinit
*
*-
hseek: move.w (ctrack).l,d7 ; dest track = 'ctrack'
hseek1: move.w #-6,(curr_err).l ; possible error = "seek error"
move.w #$86,(a6) ; write destination track# to data reg
bsr wdiskctl
move.w #$10,d6 ; execute "seek" command
bra flopcmds ; (without verify...)
*+
* reseek - home head, then reseek track
* Returns: EQ/NE on success/failure
* Falls-into: go2track
*
*-
reseek: move.w #-6,(curr_err).l ; set "seek error"
IF ROM_STBOOK
bsr.s restore ; restore head
bne.s go2trr ; (punt if home fails)
ELSE
bsr restore ; restore head
bne go2trr ; (punt if home fails)
ENDIF
clr.w 2(a1) ; current track = 0
move.w #$82,(a6) ; set "current track" reg on 1770
clr.w d7
bsr wdiskctl
move.w #$86,(a6) ; seek out to track five
move.w #5,d7
bsr wdiskctl ; dest track = 5
move.w #$10,d6
bsr.s flopcmds ; seek
bne.s go2trr ; return error on seek failure
move.w #5,2(a1) ; set current track#
*+
* go2track - seek proper track
* Passed: Current floppy parameters (ctrack, et al.)
* Returns: EQ/NE on success/failure
* Calls: flopcmds
*-
go2track: move.w #1,(fseekrtr).l ; set seek retry count
go2track1: move.w #-6,(curr_err).l ; set "seek error"
move.w #$86,(a6) ; set destination track# in
move.w (ctrack).w,d7 ; 1770's data register
bsr wdiskctl ; (write track#)
moveq #$14,d6 ; execute 1770 "seek_with_verify"
bsr.s flopcmds ; (include seek-rate bits)
bcs.s go2trr
bne.s go2track2 ; return error on seek failure
and.b #$18,d7 ; check for RNF, CRC_error, lost_data
beq.s go2track3 ; (exit on no error)
go2track2: move.w 4(a1),d0
and.w #3,d0
eori.w #3,d0 ; toggle between SD/HD mode for a second seek try
IF ROM_STBOOK
move.w d0,4(a1)
bsr hdselect ; select SD/HD mode of floppy
ELIF ROM_TOS206
move.w d0,4(a1)
tst.b (STEFlag).l ; no STE hardware available?
bne.s go2track2b ; (correct)
move.w d0,(fdccs).w
go2track2b:
ELIF ROM_TOS306
move.w d0,(fdccs).w
move.w d0,4(a1)
ENDIF
subq.w #1,(fseekrtr).l ; --seek retry count
bne.s go2trr ; (out of retries -- punt)
bsr.s restore ; move head to track 00 to reset it
bra.s go2track1 ; try again
go2track3: move.w (ctrack).w,2(a1) ; update current track number
clr.w d7 ; set EQ
go2trr: rts
*+
* restore - home head
* Passed: nothing
* Returns: EQ/NE on success/failure
* Falls-into: flopcmds
*-
restore: clr.w d6 ; $00 = 1770 "restore" command
bsr.s flopcmds ; do restore
bne.s res_r ; punt on timeout
btst #2,d7 ; test TRK00 bit
eori #4,ccr ; flip Z bit (return NE if bit is zero)
bne.s res_r ; (punt if didn't win)
clr.w 2(a1) ; set current track#
res_r: rts
*--- seek rate conversion table for HD mode
* HD is using twice the clock speed (16MHz instead of 8MHz) for the FDC,
* so the FDC has to seek "slower"
dseekrt: DC.B $01,$01,$00,$00 ; 6ms and 12ms -> 12ms, 2ms and 3ms -> 6ms
*+
* flopcmds - floppy command (on-in seek speed bits from database)
* Passed: d6.w = 1770 command
* Sets-up: seek bits (bits 0 and 1) in d6.w
* Falls-into: flopcmds
* Returns: EQ/NE on success/failure
*-
flopcmds: move.w 6(a1),d0 ; get floppy's seek rate bits
and.w #3,d0 ; OR into command
tst.w 4(a1) ; SD mode?
beq.s flopcmds1 ; (yes)
lea (dseekrt).l,a0 ; use a HD-compatible seek rate table
move.b (a0,d0.w),d0
flopcmds1: or.b d0,d6
or.b (a1),d6 ; (a1) == 8 for HD, with sets the verify bit in the command
IF ROM_STBOOK
move.w 4(a1),d0 ; get SD/HD mode
bsr.s hdselect ; select SD/HD mode of floppy
ELSE
IF ROM_TOS206
tst.b (STEFlag).l ; no STE hardware available?
bne.s flopcmds2 ; (correct)
ENDIF
move.w 4(a1),(fdccs).w
flopcmds2:
ENDIF
*+
* flopcmd - execute 1770 command (with timeout)
* Passed: d6.w = 1770 command
*
* Returns: EQ/NE on success/failure
* d7 = 1770 status bits
*
*-
flopcmd: move.l (_hz_200).w,d7 ; setup timeout (assume short)
add.l #300,d7
move.w #$80,(a6) ; select 1770 command register
bsr rdiskctl ; read it to clobber READY status
btst #7,d0 ; is motor on?
bne.s flopcm ; (yes, keep short timeout)
move.l (_hz_200).w,d7 ; extra timeout for motor startup
add.l #600,d7
flopcm: bsr wdiskct6 ; write command (in d6)
flopc1: cmp.l (_hz_200).w,d7 ; timeout?
bcs.s flopcto ; (yes, reset and return failure)
btst #5,(gpip).w ; 1770 completion?
bne.s flopc1 ; (not yet, so wait some more)
bsr rdiskct7 ; return EQ + 1770 status in d7
clr.w d6
rts
flopcto: bsr.s reset1770 ; bash controller
moveq #0,d6
subq.w #1,d6 ; and return NE
rts
*+
* hdselect - select HD mode for 1.4MB floppies and SD for 720kd floppies
* Passed: d0.w SD/HD mode
*-
IF ROM_STBOOK
hdselect: tst.w d0 ; if != 0 then select HD
bne.s hdselect2
clr.b d0 ; SD mode
bra.s hdselect3
hdselect2: move.b #$80,d0 ; HD mode is bit 7 in PORT A on the sound chip
hdselect3: move sr,d2 ; save status register
move.b #14,(psgsel).w ; select port on GI chip
move.b (psgsel).w,d1 ; get current bits
bclr #7,d1 ; clear HD bit
or.b d0,d1 ; or-in our new bit
move.b #14,(psgsel).w ; select port on GI chip
move.b d1,(psgwr).w ; and write 'em back out there
move d2,sr ; restore status register
rts
ENDIF
*+
* reset1770 - reset disk controller after a catastrophe
* Passed: nothing
* Returns: nothing
* Uses: d7
*-
reset1770: move.w #$80,(a6) ; execute 1770 "reset" command
move.w #$d0,d7
bsr wdiskctl
IF ROM_TOS306
move.w d0,-(sp)
move.w #$114,d0
bsr mfpdelay
move.w (sp)+,d0
ELSE
move.w #2,d0
r1770a: move.b (tcdr).w,d1 ; wait for 1770 to stop convulsing
r1770b: cmp.b (tcdr).w,d1
beq.s r1770b
dbra d0,r1770a
ENDIF
bsr.s rdiskct7 ; return 1770 status in d7
rts
*+
* select - setup drive select, 1770 and DMA registers
* Passed: cside, cdev
* Returns: appropriate drive and side select
*-
select: clr.w (deselflg).w ; floppies NOT deselected
move.w (cdev).w,d0 ; get device number
addq.b #1,d0 ; add and shift to get select bits
lsl.b #1,d0 ; into bits 1 and 2
or.w (cside).w,d0 ; or-in side number (bit 0)
eori.b #7,d0 ; negate bits for funky hardware select
and.b #7,d0 ; strip anything else out there
bsr.s setporta ; do drive select
move.w #$82,(a6) ; setup 1770 track register
move.w 2(a1),d7 ; for current track number
bsr.s wdiskctl
select1: move.w #$84,(a6) ; setup requested sector_number from
move.w (csect).w,d7 ; caller's parameters
bsr.s wdiskctl
move.b (cdma+3).w,(dmalow).w ; setup DMA chip's DMA pointer
move.b (cdma+2).w,(dmamid).w
move.b (cdma+1).w,(dmahigh).w
rts
*+
* setporta - set floppy select bits in PORT A on the sound chip
* Passed: d0.b (low three bits)
* Returns: d1 = value written to port A
* d2 = old value read from port A
* Uses: d1
*-
setporta: move sr,-(sp) ; save our IPL
ori #$700,sr ; start critical section
move.b #14,(psgsel).w ; select port on GI chip
move.b (psgsel).w,d1 ; get current bits
move.b d1,d2 ; save old bits for caller
and.b #$f8,d1 ; strip low three bits there
or.b d0,d1 ; or-in our new bits
move.b d1,(psgwr).w ; and write 'em back out there
move (sp)+,sr ; restore IPL to terminate CS, return
rts
*+
* Primitives to read/write 1770 controller chip (DISKCTL register).
*
* The 1770 can't keep up with full-tilt CPU access, so
* we have to surround read and writes with delay loops.
* This is not really as slow as it sounds.
*
*-
wdiskct6: bsr.s rwdelay ; delay
move.w d6,(diskctl).w ; write d6 to diskctl
rts
wdiskctl: bsr.s rwdelay ; delay
move.w d7,(diskctl).w ; write d7 to diskctl
rts
rdiskct7: bsr.s rwdelay ; delay
move.w (diskctl).w,d7 ; read diskctl into d7
rts
rdiskctl: bsr.s rwdelay ; delay
move.w (diskctl).w,d0 ; read diskctl into d0
rts
rwdelay:
IF ROM_TOS306
move.w d0,-(sp)
move.w #$119,d0
bsr mfpdelay
move.w (sp)+,d0
ELSE
movem.l d0-d1,-(sp) ; save registers
move.w #2,d0
rwdly1: move.b (tcdr).w,d1 ; busy-loop: give 1770 time to settle
rwdly2: cmp.b (tcdr).w,d1
beq.s rwdly2
dbra d0,rwdly1
movem.l (sp)+,d0-d1 ; restore registers
ENDIF
rts
*+
* change - check to see if the "right" floppy bas been inserted
* On the stack:
* $10(sp) - dev.W (device#)
* $c(sp) - dsb.L (pointer to Device State Block)
* 8(sp) - dma.L (dma pointer)
* 4(sp) - ret1.L (caller's return address)
* 0(sp) - ret.L (change's return address)
*
* Returns: both media "might have changed" condition
*
* Uses: C registers
*
*-
change: cmpi.w #1,(_nflops).l ; if there are zero or two floppies
bne.s ch_r ; then do nothing (return OK)
move.w $10(sp),d0 ; if cdev == _curflop
cmp.w (_curflop).l,d0 ; (...current disk == current drive?)
beq.s ch_ok1 ; then return OK (but use drive #0)
*--- ask the user to stick in the other floppy (via critical error handler)
push disk# we want inserted:move.w d0,-(sp)
move.w #-17,-(sp) ; push "INSERT_A_DISK" error number
bsr crit_err ; use critical error handler and
addq.w #4,sp ; hope somebody handles it
move.w #$ffff,(wplatch).l ; set "might have changed" on both drvs
lea (_acctim).l,a0
clr.l (a0)+
clr.l (a0)
move.w $10(sp),(_curflop).l ; set current disk#
ch_ok1: clr.w $10(sp) ; use drive 0
ch_r: rts
*+
* setdmode - set drive-change mode
* Passed: d0.b = mode to put current drive in (0, 1, 2)
* Uses: a0
*
*-
setdmode: lea (diskmode).w,a0 ; a0 -> disk mode table
move.b d0,-(sp) ; (save mode)
move.w (cdev).w,d0 ; d0.w = drive# (index into table)
cmpi.w #1,(_nflops).l
bne.s setdmode2
move.w (_curflop).l,d0
setdmode2: move.b (sp)+,(a0,d0.w) ; set drive's mode
rts
*+
* floprate - sets the seek rate of the specified floppy drive
* On the stack:
* 6(sp) - rate.w (0: 6ms, 1: 12ms, 2:2ms, 3:3ms)
* 4(sp) - dev.W (device#)
* 0(sp) - return address
*
* Returns: prior seek rate for the specified drive
*
*-
*--- pick a DSB
floprate: lea (dsb0).l,a1
tst.w 4(sp)
beq.s floprate2
lea (dsb1).l,a1
floprate2: move.w 6(a1),d0 ; current rate
move.w 6(sp),d1 ; new seek rate
cmp.w #-1,d1 ; new seek rate == -1
beq.s floprateret ; (just return the current rate)
cmp.w #-2,d1 ; new seek rate == -2
beq.s flopratehd ; (select HD floppy density)
cmp.w #-3,d1 ; new seek rate == -3
beq.s flopratesd ; (select SD floppy density)
cmp.w #-4,d1 ; new seek rate == -4
beq.s floprateretdens ; (return current density)
move.w d1,6(a1) ; set new seek rate in dsb
floprateret:ext.l d0
rts
flopratehd: move.b #8,(a1) ; set density to HD
moveq #0,d0 ; return no error
rts
flopratesd: clr.b (a1) ; set density to SD
moveq #0,d0 ; return no error
rts
floprateretdens:tst.b (a1) ; return current density setting
sne d0 ; -1:HD, 0:SD
ext.w d0
ext.l d0
rts
*+
* Fast RAM doesn't support DMA transfers, we use a temporary buffer
* for loads/stores from and into Fast RAM. The buffer is in _FRB cookie.
*-
frbbackcopy:move.w (frbufscnt).l,d1 ; sectors to copy back into memory?
beq.s frbcheckrts ; (no, punt)
IF ROM_TOS306
move.l d0,-(sp)
bsr flushCaches
move.l (sp)+,d0
ENDIF
clr.w (frbufscnt).l ; reset sector flag
movea.l (frbufaddress).l,a0 ; requested buffer address in memory
movea.l (frbufcookie).l,a1 ; used DMA buffer address
frbcopya1_a0:asl.w #5,d1
subq.w #1,d1
frbcopyl: move.l (a1)+,(a0)+
move.l (a1)+,(a0)+
move.l (a1)+,(a0)+
move.l (a1)+,(a0)+
dbra d1,frbcopyl
frbcheckrts:rts
frbcheckrd:
IF ROM_TOS306
cmpi.l #$a00000,8(sp) ; beyond the 10MB limit?
ELSE
cmpi.l #$400000,8(sp) ; beyond the 4MB limit?
ENDIF
bcs.s frbcheckrts ; (no, punt)
bsr.s findFRBc
move.l 8(sp),(frbufaddress).l ; save buffer address
move.l (frbufcookie).l,8(sp) ; inject FRB buffer into the parameter
move.w $18(sp),(frbufscnt).l ; save number of sectors
rts
frbcheckwr:
IF ROM_TOS306
cmpi.l #$a00000,8(sp) ; beyond the 10MB limit?
ELSE
cmpi.l #$400000,8(sp) ; beyond the 4MB limit?
ENDIF
bcs.s frbcheckrts ; (no, punt)
bsr.s findFRBc
movea.l 8(sp),a1 ; get the source buffer address
movea.l (frbufcookie).l,a0
move.l a0,8(sp) ; inject FRB buffer into the parameter
move.w $18(sp),d1 ; number of sectors
bra.s frbcopya1_a0 ; copy D1 sectors from A1 to A0
frbcheckfmt:
IF ROM_TOS306
cmpi.l #$a00000,8(sp) ; beyond the 10MB limit?
ELSE
cmpi.l #$400000,8(sp) ; beyond the 4MB limit?
ENDIF
bcs.s frbcheckrts ; (no, punt)
bsr.s findFRBc
move.l 8(sp),(frbufaddress).l ; save buffer address
move.l (frbufcookie).l,8(sp) ; inject FRB buffer into the parameter
move.w #1,(frbufscnt).l ; 1 sector
rts
* get FRB buffer pointer from the cookies
findFRBc: tst.l (frbufcookie).l ; variable already set?
bne.s findFRBcrts ; (yes, return)
movea.l (_p_cookies).w,a0
cmpa.w #0,a0 ; no cookie jar?
beq.s findFRBcx
findFRBcl: tst.l (a0) ; end of the cookie jar?
beq.s findFRBcx
cmpi.l #'_FRB',(a0)+
beq.s findFRBcf
addq.l #4,a0
bra.s findFRBcl
findFRBcf: move.l (a0)+,(frbufcookie).l
findFRBcrts:rts
findFRBcx: addq.l #8,sp ; go 2 return levels up
moveq #-12,d0 ; return -12 if no _FRB cookie
rts