mirror of
https://github.com/a2stuff/prodos-drivers.git
synced 2024-06-02 01:41:31 +00:00
Compare commits
173 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1ed40ff2dc | ||
|
1d6b923783 | ||
|
7aeb2e55ad | ||
|
7bbc96924b | ||
|
537d79810b | ||
|
477c1f7764 | ||
|
b8ef97b42d | ||
|
764b4bd8fd | ||
|
93e48784b9 | ||
|
01175cf573 | ||
|
18a73004c4 | ||
|
ed2fa6e7d2 | ||
|
a519d0c619 | ||
|
b182a53dfa | ||
|
9401723f0d | ||
|
ec4a0eeb1e | ||
|
caeb237321 | ||
|
c01d26042a | ||
|
8ca57b2fac | ||
|
e706385fe9 | ||
|
774889ec2e | ||
|
5211dc6296 | ||
|
ee5fbeda2d | ||
|
d37691dead | ||
|
1ddae4fab7 | ||
|
d9e72d3c1e | ||
|
01fed0d524 | ||
|
dc9c77170e | ||
|
f64011338d | ||
|
f2ea11fcde | ||
|
6f142c08d6 | ||
|
3a085d0d60 | ||
|
8f658437ab | ||
|
c4c14419fe | ||
|
ac4b655661 | ||
|
26bde8abae | ||
|
f0abdcc738 | ||
|
9590bc4b9e | ||
|
145aba1f66 | ||
|
3b8fe3c461 | ||
|
188f1b4ce8 | ||
|
536ed14b60 | ||
|
c323b205bc | ||
|
b1c3d52e0a | ||
|
a6d4fa6308 | ||
|
3e0d41b809 | ||
|
f740575697 | ||
|
392ab0b7ef | ||
|
0cd347b737 | ||
|
5b0ef800ce | ||
|
ea853d7af1 | ||
|
94fae50608 | ||
|
39c7f83e04 | ||
|
8dbdd58d38 | ||
|
a7e32d62d4 | ||
|
b621ac6a4d | ||
|
7c8c608b65 | ||
|
974a7d49f0 | ||
|
2358dd7bab | ||
|
46aef091df | ||
|
1a3aa1e8c6 | ||
|
b74244bbe1 | ||
|
304befcab2 | ||
|
ae13e28777 | ||
|
6e9c18c3aa | ||
|
5c71155dc3 | ||
|
73ac9cbfc0 | ||
|
3aedc2ad21 | ||
|
9b898fca75 | ||
|
ace258d862 | ||
|
1312b3f386 | ||
|
5cf3426967 | ||
|
cc49835c9f | ||
|
1260999b6c | ||
|
a42dec7f3f | ||
|
4d24855217 | ||
|
4975081bb6 | ||
|
3b7b2c0b9c | ||
|
ac4fccd420 | ||
|
d9e90c0fe6 | ||
|
095f87813e | ||
|
cf891e061f | ||
|
d10429b3a0 | ||
|
0f41276584 | ||
|
cd2074c864 | ||
|
0626869e27 | ||
|
78e1d2d2e6 | ||
|
8a242abc17 | ||
|
e34e7a9728 | ||
|
eea2f6c343 | ||
|
f31f6775e9 | ||
|
ec94293ec9 | ||
|
ffa1495a11 | ||
|
2fab1e19bc | ||
|
771376f312 | ||
|
d049ae6dd8 | ||
|
0aaf557aab | ||
|
db977835ac | ||
|
2769d1d7a6 | ||
|
eb3ca7e7e2 | ||
|
cfb8cab3b2 | ||
|
c7df8cb73b | ||
|
850b10059d | ||
|
783c2af304 | ||
|
7cc70cd729 | ||
|
ba0c5e53d3 | ||
|
64c8ff73e3 | ||
|
fbddc217a6 | ||
|
e65fdfe1b5 | ||
|
9dc6a7a149 | ||
|
7af95f7162 | ||
|
291d0f12f2 | ||
|
06cbcd4fa9 | ||
|
16599fc4b9 | ||
|
754d267c1e | ||
|
3c9c491bae | ||
|
b5b5b96d2a | ||
|
2dfb4410a9 | ||
|
f380680cdd | ||
|
d4466231f5 | ||
|
9b98a09067 | ||
|
88472a8e11 | ||
|
903cd4ff1e | ||
|
d115882280 | ||
|
6edbfa48ac | ||
|
9d424a4e01 | ||
|
4d4a164e30 | ||
|
a2bc49a38b | ||
|
fbd79badee | ||
|
feaac6d4fc | ||
|
f6e359cfb7 | ||
|
e974c76ff7 | ||
|
23af1ff081 | ||
|
5aae9d79bc | ||
|
e878aec51a | ||
|
3ee2f9ef3f | ||
|
3a29c86c4b | ||
|
320edcb634 | ||
|
b064242a7a | ||
|
c11c49a03b | ||
|
37597de535 | ||
|
c0c9cc01dd | ||
|
fa0bfcd936 | ||
|
4cc70803fd | ||
|
f0ceec0aea | ||
|
1acc9e5946 | ||
|
72b8859222 | ||
|
5e28639a4e | ||
|
b199b21f77 | ||
|
d74dec19b2 | ||
|
6eb9613a28 | ||
|
5e3d46373e | ||
|
6df7941617 | ||
|
b7074e04f7 | ||
|
6a0a6c5ad9 | ||
|
ece57e671c | ||
|
589bc09605 | ||
|
714c796605 | ||
|
dd6a4b319e | ||
|
1d974dcf34 | ||
|
00a2275346 | ||
|
ba96487609 | ||
|
7042ad9901 | ||
|
e6a02b099c | ||
|
b6d7b3068a | ||
|
106b31b899 | ||
|
89edcb917b | ||
|
b204ea7f2f | ||
|
82a8ade241 | ||
|
987092c318 | ||
|
e13d9f0e60 | ||
|
2db6f978e4 | ||
|
8817199aa7 |
31
.github/workflows/main.yml
vendored
Normal file
31
.github/workflows/main.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
tags: ['*']
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: a2stuff/build-install-ca65-action@v2
|
||||
- uses: a2stuff/build-install-cadius-action@v1
|
||||
|
||||
- name: build
|
||||
env:
|
||||
TERM: xterm-256color
|
||||
run: >
|
||||
make && make package
|
||||
|
||||
- name: deploy new version
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: "prodos-drivers.po"
|
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -1,7 +1,16 @@
|
|||
# Generated files
|
||||
*.BIN
|
||||
*.SYS
|
||||
*.list
|
||||
|
||||
# Reference copy of original binary
|
||||
orig
|
||||
|
||||
# Output files
|
||||
*.SYS
|
||||
*.BIN
|
||||
*.list
|
||||
# Build directory
|
||||
out
|
||||
|
||||
# Directory mounted in Virtual ][ as a ProDOS volume
|
||||
mount
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
|
|
38
Makefile
38
Makefile
|
@ -1,27 +1,25 @@
|
|||
targets := clocks selectors accelerators ram.drv setup util textcolors
|
||||
|
||||
CC65 = ~/dev/cc65/bin
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
CCFLAGS = --config apple2-asm.cfg
|
||||
.PHONY: all $(targets) package
|
||||
|
||||
TARGETS = prodos.mod.BIN ns.clock.system.SYS cricket.system.SYS test.BIN get.time.BIN
|
||||
all: $(targets)
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%m")
|
||||
DD = $(shell date "+%d")
|
||||
YY = $(shell date "+%y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY)
|
||||
export LOG_SUCCESS=1
|
||||
export LOG_FAILURE=1
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(TARGETS)
|
||||
|
||||
HEADERS = $(wildcard *.inc)
|
||||
# Build all targets
|
||||
$(targets):
|
||||
@tput setaf 3 && echo "Building: $@" && tput sgr0
|
||||
@$(MAKE) -C $@ \
|
||||
&& (tput setaf 2 && echo "make $@ good" && tput sgr0) \
|
||||
|| (tput blink && tput setaf 1 && echo "MAKE $@ BAD" && tput sgr0 && false)
|
||||
|
||||
# Clean all temporary/target files
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f $(TARGETS)
|
||||
@for dir in $(targets); do \
|
||||
tput setaf 2 && echo "cleaning $$dir" && tput sgr0; \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
||||
|
||||
%.o: %.s $(HEADERS)
|
||||
$(CC65)/ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
%.BIN %.SYS: %.o
|
||||
$(CC65)/ld65 $(CCFLAGS) -o $@ $<
|
||||
package:
|
||||
./package.sh
|
||||
|
|
124
README.md
124
README.md
|
@ -1,48 +1,112 @@
|
|||
# The Cricket! - ProDOS Clock Driver
|
||||
# ProDOS Drivers
|
||||
|
||||
I acquired a Cricket sound/clock peripheral on eBay. Therefore it is now critical that we have a conforming ProDOS clock driver for it.
|
||||
[![build](https://github.com/a2stuff/prodos-drivers/actions/workflows/main.yml/badge.svg)](https://github.com/a2stuff/prodos-drivers/actions/workflows/main.yml)
|
||||
|
||||
> STATUS: Works on my machine!
|
||||
Build with [ca65](https://cc65.github.io/doc/ca65.html)
|
||||
|
||||
## Background
|
||||
# What are ProDOS "drivers"?
|
||||
|
||||
"The Cricket!" by Street Electronics Corporation, released in 1984, is a hardware peripheral for the Apple //c computer. It plugs into the serial port and offers a multi-voice sound synthesizer, a speech synthesizer, and a real-time clock.
|
||||
The ProDOS operating system for the Apple II executes the first `.SYSTEM` file found in the boot directory on startup. A common pattern is to have the boot directory contain several "driver" files that customize ProDOS by installing drivers for hardware or modify specific parts of the operating system. These include:
|
||||
|
||||
The disks supplied with the device include:
|
||||
* `/CRICKET/PRODOS.MOD` which can be BRUN to patch ProDOS in memory with a clock driver.
|
||||
* A modified version of ProDOS
|
||||
* A utility to patch ProDOS on disk
|
||||
* Real-time Clock drivers (e.g. No-Slot Clock, Cricket!, AE DClock, etc)
|
||||
* In ProDOS 1.x, 2.0 and 2.4 the Thunderclock driver is built-in.
|
||||
* RAM Disk drivers (e.g. RamWorks)
|
||||
* In ProDOS 1.x, 2.0 and 2.4 only a 64K driver for /RAM is built-in.
|
||||
* Quit dispatcher/selector (`BYE` routines)
|
||||
* In ProDOS 1.0 and later, a 40-column friendly [selector](selector) prompts for a prefix then a path `ENTER PREFIX (PRESS "RETURN" TO ACCEPT)`
|
||||
* In ProDOS 1.9 and 2.0.x, on 80-column systems, a menu-driven selector is installed instead.
|
||||
* In ProDOS 2.4.x [Bitsy Bye](https://prodos8.com/bitsy-bye/) is built-in.
|
||||
|
||||
## Goals
|
||||
Early versions of these drivers would often invoke a specific file on completion, sometimes user-configurable. The best versions of these drivers simply execute the following `.SYSTEM` file, although this is non-trivial code and often did not work with network drives.
|
||||
|
||||
Like the `NS.CLOCK.SYSTEM` (by "CAP") ideally we would have:
|
||||
This repository collects several drivers and uses common code to chain to the next `.SYSTEM` file, suporting network drives.
|
||||
|
||||
* [x] A ProDOS `.SYSTEM` file
|
||||
* [x] Detects the presence of a Cricket
|
||||
* [x] Installs a driver in memory following the ProDOS clock driver protocol
|
||||
* [x] Chains to the next `.SYSTEM` file (e.g. `BASIC.SYSTEM`)
|
||||
## What is present here?
|
||||
|
||||
Successfully tested on real hardware. (Laser 128EX, an Apple //c clone — including at 3x speed!)
|
||||
This repo includes the following drivers/modifications:
|
||||
|
||||
## Build
|
||||
* Real-time Clock drivers
|
||||
* No-Slot Clock
|
||||
* Cricket!
|
||||
* Applied Engineering DClock
|
||||
* ROMX Real-Time Clock
|
||||
* FujiNet Clock
|
||||
* A "jumbo" driver that includes all of the above (just called `CLOCK.SYSTEM`)
|
||||
* Accelerators
|
||||
* ZIP CHIP configuration (sets the chip to slow on speaker access)
|
||||
* RAM Disk drivers
|
||||
* RAMWorks Driver by Glen E. Bredon
|
||||
* Quit dispatcher/selector (`BYE` routines)
|
||||
* 40-column Selector (from ProDOS)
|
||||
* 80-column menu-driven Selector (from ProDOS 1.9 and 2.x)
|
||||
* Bird's Better Bye (a 40-column menu-driven selector)
|
||||
* Buh-Bye (an enhanced version of the ProDOS 80-column, menu-driven selector)
|
||||
* Text color themes
|
||||
* These set the IIgs (or VidHD) text/background/border colors
|
||||
|
||||
Requires [cc65](https://github.com/cc65/cc65). The included `Makefile` is very specific to my machine - sorry about that.
|
||||
In addition, `QUIT.SYSTEM` is present which isn't a driver but which immediately invokes the QUIT handler (a.k.a. program selector). This will happen automatically if the last driver can't find another `.SYSTEM` file, but `QUIT.SYSTEM` can be used to stop the chain.
|
||||
|
||||
[CRICKET.SYSTEM](cricket.system.s) is the result of the build.
|
||||
There's also `PAUSE.SYSTEM` which just waits for a fraction of a second before invoking the next driver file. (Why? In case the log messages from the other installers goes by too fast!)
|
||||
|
||||
## Notes
|
||||
Non-drivers that are included:
|
||||
* The `DATE` binary file can be `BRUN` (or just `-DATE`) to show the current ProDOS Date/Time, to verify that the clock driver is working.
|
||||
* Some utilities for The Cricket! clock are also included.
|
||||
|
||||
I ended up disassembling both [NS.CLOCK.SYSTEM](ns.clock.system.s) (to understand the SYSTEM chaining - what a pain!) and The Cricket!'s [PRODOS.MOD](prodos.mod.s) and melding them together, adding in the detection routine following the protocol in the manual.
|
||||
## How do you use these?
|
||||
|
||||
Other files:
|
||||
* [GET.TIME](get.time.s) just prints the current ProDOS date/time, to verify the time is set and updating.
|
||||
* [TEST](test.s) attempts to identify an SSC in Slot 2 and the Cricket via the ID sequence, to test routines.
|
||||
The intent is that you use a tool like Copy II Plus or [Apple II DeskTop](https://github.com/a2stuff/a2d) to copy and arrange the SYSTEM files on your boot disk as you see fit. A boot disk image catalog that is used on multiple different hardware configurations might include:
|
||||
|
||||
## Resources
|
||||
* `PRODOS` - the operating system, e.g. [ProDOS 2.4](https://prodos8.com/)
|
||||
* `NS.CLOCK.SYSTEM` - install No-Slot clock driver, if present
|
||||
* `ROMXRTC.SYSTEM` - install ROMX clock driver, if present
|
||||
* `FN.CLOCK.SYSTEM` - install FujiNet clock driver, if present
|
||||
* `DCLOCK.SYSTEM` - install DClock clock driver, if present
|
||||
* `CRICKET.SYSTEM` - install Cricket! clock driver, if present
|
||||
* `ZIPCHIP.SYSTEM` - slow the ZIP CHIP on speaker access, if present
|
||||
* `RAM.DRV.SYSTEM` - install RamWorks RAM disk driver, if present
|
||||
* `BUHBYE.SYSTEM` - install a customized Quit handler to replace the built-in one
|
||||
* `PAUSE.SYSTEM` - pause for a moment, so that you can inspect the output of the above
|
||||
* `QUIT.SYSTEM` - invoke the Quit handler immediately, as a program selector
|
||||
* `BASIC.SYSTEM` - which will not be automatically invoked, but is available to manually invoke
|
||||
|
||||
Cricket disks on Asimov:
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/images/hardware/sound/cricket_disk1.po
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/images/hardware/sound/cricket_disk2.po
|
||||
Alternately, you might want to install some drivers then immediately launch into BASIC. In that case, put `BASIC.SYSTEM` after the drivers in place of `QUIT.SYSTEM`.
|
||||
|
||||
Cricket Manual on Asimov:
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/documentation/hardware/sound/Street%20Electronics%20The%20Cricket.pdf
|
||||
## Alternate Approach
|
||||
|
||||
If you want to keep your volume directory tidier, consider using [SETUP.SYSTEM](./setup/README.md) instead.
|
||||
|
||||
# Building
|
||||
|
||||
Fetch, build, and install [cc65](http://cc65.github.io/cc65/):
|
||||
|
||||
```
|
||||
git clone https://github.com/cc65/cc65
|
||||
make -C cc65 && make -C cc65 avail
|
||||
```
|
||||
|
||||
Fetch and build this repo:
|
||||
|
||||
```
|
||||
git clone https://github.com/a2stuff/prodos-drivers
|
||||
cd prodos-drivers
|
||||
make
|
||||
```
|
||||
|
||||
To make a disk image, fetch, build and install [Cadius](https://github.com/mach-kernel/cadius):
|
||||
|
||||
```
|
||||
git clone https://github.com/mach-kernel/cadius
|
||||
make -C cadius && make -C cadius install
|
||||
```
|
||||
|
||||
Then you can:
|
||||
|
||||
```
|
||||
cd prodos-drivers
|
||||
make && make package
|
||||
```
|
||||
|
||||
This will produce `prodos-drivers.po`, a disk image for use with emulators or tools like [ADTPro](http://adtpro.com/).
|
||||
|
||||
Notes:
|
||||
* Specify `LOG_SUCCESS=0` and/or `LOG_FAILURE=0` (e.g. `make LOG_SUCCESS=0`) to build with driver success and/or error logging suppressed.
|
||||
|
|
22
accelerators/Makefile
Normal file
22
accelerators/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
targets := zipchip
|
||||
|
||||
.PHONY: all $(targets)
|
||||
|
||||
all: $(targets)
|
||||
|
||||
export LOG_SUCCESS
|
||||
export LOG_FAILURE
|
||||
|
||||
# Build all targets
|
||||
$(targets):
|
||||
@tput setaf 3 && echo "Building: $@" && tput sgr0
|
||||
@$(MAKE) -C $@ \
|
||||
&& (tput setaf 2 && echo "make $@ good" && tput sgr0) \
|
||||
|| (tput blink && tput setaf 1 && echo "MAKE $@ BAD" && tput sgr0 && false)
|
||||
|
||||
# Clean all temporary/target files
|
||||
clean:
|
||||
@for dir in $(targets); do \
|
||||
tput setaf 2 && echo "cleaning $$dir" && tput sgr0; \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
46
accelerators/zipchip/Makefile
Normal file
46
accelerators/zipchip/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/zipchip.system.SYS \
|
||||
$(OUTDIR)/zipchip.setup.SYS
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.BIN $(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
179
accelerators/zipchip/zipchip.system.s
Normal file
179
accelerators/zipchip/zipchip.system.s
Normal file
|
@ -0,0 +1,179 @@
|
|||
;;; ZIPCHIP.SYSTEM
|
||||
;;; Configures the speaker to be temporarily slow
|
||||
;;; TODO: Add a configuration utility
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;; From ZIP Chip Manual v1 1987
|
||||
ZC_REG_LOCK := $C05A
|
||||
;; Write:
|
||||
;; $A5 Locks the ZIP CHIP.
|
||||
;; 4 consecutive $5A writes unlock ZIP CHIP.
|
||||
;; While unlocked, any write other than $A5 or
|
||||
;; $5A will initiate an indefinate syncronous [sic]
|
||||
;; sequence.
|
||||
kZCLock = $A5
|
||||
kZCUnlock = $5A
|
||||
|
||||
ZC_REG_ENABLE := $C05B
|
||||
;; Write - Any hex byte written will enable ZIP CHIP
|
||||
|
||||
ZC_REG_STATUS := $C05B
|
||||
;; Read - Read the current status of the following:
|
||||
;; bit 0 & 1 - Ramsize where
|
||||
;; RAMSIZE1 RAMSIZE0 SIZE
|
||||
;; 0 0 8K
|
||||
;; 0 1 16K
|
||||
;; 1 0 32K
|
||||
;; 1 1 64K
|
||||
;; bit 2 - unused
|
||||
;; bit 3 - Delay (for memory)
|
||||
;; 0 = Fast Mode - Delay not in effect
|
||||
;; 1 = Sync Mode - Delay in effect
|
||||
;; bit 4 - Disabled/enabled
|
||||
;; 0 = Chip Enabled
|
||||
;; 1 = Chip Disabled
|
||||
;; bit 5 - Paddle fast/normal
|
||||
;; 0 = Fast Mode
|
||||
;; 1 = Synchronous Mode (Normal)
|
||||
;; bit 6 = Cache Updated by data read
|
||||
;; 0 = No update
|
||||
;; 1 = Yes cache updated
|
||||
;; bit 7 = Clock Pulse - 1.0035 milliseconds
|
||||
;; Edges occur at .50175 milliseconds
|
||||
|
||||
ZC_REG_SLOTSPKR := $C05C
|
||||
;; Read/Write - Slow/Speaker set and read
|
||||
;; 0 = Set slot/speaker Fast
|
||||
;; 1 = Set slot/speaker Normal
|
||||
;; bit 0 - Speaker bit 4 - Slot 4
|
||||
;; bit 1 - Slot 1 bit 5 - Slot 5
|
||||
;; bit 2 - Slot 2 bit 6 - Slot 6
|
||||
;; bit 3 - Slot 3 bit 7 - Slot 7
|
||||
|
||||
ZC_REG_SYSSPEED := $C05D
|
||||
;; Write - Set System Speed
|
||||
;; bit 0 - unused bit 4 - Clk4/5
|
||||
;; bit 1 - unused bit 5 - Clk5/6
|
||||
;; bit 2 - Clk2/3 bit 6 - Clk/2
|
||||
;; bit 3 - Clk3/4 bit 7 - Clk/4
|
||||
;; NOTE: bit 6 and bit 7 yield Clk/3
|
||||
|
||||
ZC_REG_SYNCOP := $C05E
|
||||
;; Write - Enable/Disable Synchronous Operation
|
||||
;; for I/O Devices
|
||||
;; bit 0 through bit 6 - Not Used
|
||||
;; bit 7 - Enable/Disable Delay
|
||||
;; 0 = Enable Delay
|
||||
;; 1 = Disable and Reset Delay
|
||||
|
||||
ZC_REG_SOFTSW := $C05E
|
||||
;; Read - Read Apple softswitches
|
||||
;; 0 = False
|
||||
;; 1 = True
|
||||
;; bit 0 - ROMRD bit 4 - 80STORE
|
||||
;; bit 1 - RAMBNK bit 5 - MWR
|
||||
;; bit 2 - PAGE2 bit 6 - MRD
|
||||
;; bit 3 - HIRES bit 7 - ALTZP
|
||||
|
||||
ZC_REG_PDLBSLC := $C05F
|
||||
;; Write - Paddle Speed, Bank Switch Language Card
|
||||
;; bit 0 through bit 5 - Not Used
|
||||
;; bit 6 - Paddle Set
|
||||
;; 0 = Disable Paddle Delay
|
||||
;; 1 = Enable Paddle Delay
|
||||
;; bit 7 - Language Card Enable/Disable
|
||||
;; 0 = Enable Cache of Language Card Memory
|
||||
;; 1 = Disable Cache of Language Card Memory
|
||||
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "ZIP CHIP"
|
||||
|
||||
.proc maybe_install_driver
|
||||
php ; timing sensitive
|
||||
sei
|
||||
|
||||
;; Unlock
|
||||
lda #kZCUnlock
|
||||
sta ZC_REG_LOCK
|
||||
sta ZC_REG_LOCK
|
||||
sta ZC_REG_LOCK
|
||||
sta ZC_REG_LOCK
|
||||
|
||||
;; ZIP CHIP present?
|
||||
lda ZC_REG_SLOTSPKR
|
||||
eor #$FF
|
||||
sta ZC_REG_SLOTSPKR
|
||||
cmp ZC_REG_SLOTSPKR
|
||||
bne no_zip
|
||||
eor #$FF
|
||||
sta ZC_REG_SLOTSPKR
|
||||
cmp ZC_REG_SLOTSPKR
|
||||
bne no_zip
|
||||
|
||||
;; Slow on speaker access
|
||||
lda ZC_REG_SLOTSPKR
|
||||
ora #%00000001 ; bit 0 = Speaker
|
||||
sta ZC_REG_SLOTSPKR
|
||||
|
||||
;; Get size
|
||||
lda ZC_REG_STATUS
|
||||
and #%00000011
|
||||
asl
|
||||
tax
|
||||
lda size_table,x
|
||||
sta size
|
||||
lda size_table+1,x
|
||||
sta size+1
|
||||
|
||||
;; Lock
|
||||
lda #kZCLock
|
||||
sta ZC_REG_LOCK
|
||||
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " "
|
||||
size: .res 2 ; patched with cache size
|
||||
scrcode "K - Configured."
|
||||
.byte 0
|
||||
|
||||
plp
|
||||
rts
|
||||
|
||||
no_zip:
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
|
||||
plp
|
||||
rts
|
||||
|
||||
size_table:
|
||||
scrcode " 8"
|
||||
scrcode "16"
|
||||
scrcode "32"
|
||||
scrcode "64"
|
||||
|
||||
|
||||
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
22
clocks/Makefile
Normal file
22
clocks/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
targets := ns.clock cricket dclock romx fujinet jumbo
|
||||
|
||||
.PHONY: all $(targets)
|
||||
|
||||
all: $(targets)
|
||||
|
||||
export LOG_SUCCESS
|
||||
export LOG_FAILURE
|
||||
|
||||
# Build all targets
|
||||
$(targets):
|
||||
@tput setaf 3 && echo "Building: $@" && tput sgr0
|
||||
@$(MAKE) -C $@ \
|
||||
&& (tput setaf 2 && echo "make $@ good" && tput sgr0) \
|
||||
|| (tput blink && tput setaf 1 && echo "MAKE $@ BAD" && tput sgr0 && false)
|
||||
|
||||
# Clean all temporary/target files
|
||||
clean:
|
||||
@for dir in $(targets); do \
|
||||
tput setaf 2 && echo "cleaning $$dir" && tput sgr0; \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
34
clocks/README.md
Normal file
34
clocks/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# ProDOS Clock Drivers
|
||||
|
||||
Other than the Apple IIgs model, the Apple II did not ship with a real-time system clock. Many third party [Apple II system clocks](https://en.wikipedia.org/wiki/Apple_II_system_clocks) were produced in the 1980s, and many new system clocks are available, either as entirely new designs or recreations.
|
||||
|
||||
The ProDOS operating system for the Apple II personal computer line natively supports the Thunderclock real-time clock card, but there is a protocol for custom clock drivers to be installed:
|
||||
|
||||
* Check `MACHID` bit 0 to see if a clock is already active; abort if so.
|
||||
* Optional: Probe hardware to determine if the clock is present; abort if not.
|
||||
* Relocate the clock driver to LC bank 1, at the address at `DATETIME`+1
|
||||
* Update `DATETIME` to be a `JMP` instruction.
|
||||
* Optional: Chain to the next `.SYSTEM` file.
|
||||
|
||||
In addition:
|
||||
|
||||
* The clock driver must fit into 125 bytes.
|
||||
* The driver may dirty $200-$207 but other memory must be restored if modified.
|
||||
* When invoked, the clock driver should read the clock hardware and encode the date and time into `DATELO`/`DATEHI` and `TIMELO`/`TIMEHI`.
|
||||
* ProDOS calls the clock driver when `GET_TIME` is called, and on every call (`CREATE`, `RENAME`, etc) that might need the date and time.
|
||||
|
||||
See https://prodos8.com/docs/techref/adding-routines-to-prodos/ for more information.
|
||||
|
||||
## Included drivers
|
||||
|
||||
This directory includes drivers for the following real-time clocks:
|
||||
|
||||
* [No-Slot Clock](https://www.reactivemicro.com/product/no-slot-clock/)
|
||||
* [ROMX Real-Time Clock](https://theromexchange.com/)
|
||||
* [FujiNet Clock](https://fujinet.online/)
|
||||
* DClock
|
||||
* Cricket!
|
||||
|
||||
All follow the above protocol: install only if there is not already a clock, probe for the clock before installing, and chain to the next driver.
|
||||
|
||||
In addition, the "jumbo" directory combines the other driver installers to create a single unified clock driver installer. See its [README](jumbo/README.md) for more details.
|
57
clocks/cricket/Makefile
Normal file
57
clocks/cricket/Makefile
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
UTILDIR = $(OUTDIR)/cricket.util
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(UTILDIR)/prodos.mod.BIN \
|
||||
$(OUTDIR)/cricket.system.SYS \
|
||||
$(OUTDIR)/cricket.setup.SYS \
|
||||
$(UTILDIR)/test.BIN \
|
||||
$(UTILDIR)/set.datetime.BIN \
|
||||
$(UTILDIR)/set.time.BIN \
|
||||
$(UTILDIR)/set.date.BIN
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(UTILDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR) $(UTILDIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(UTILDIR)/*.o
|
||||
rm -f $(UTILDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o $(UTILDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
|
||||
$(UTILDIR)/%.BIN: $(UTILDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
58
clocks/cricket/README.md
Normal file
58
clocks/cricket/README.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# _The Cricket!_ — ProDOS Clock Driver
|
||||
|
||||
I acquired a Cricket sound/clock peripheral on eBay. Therefore it is now critical that we have a conforming ProDOS clock driver for it.
|
||||
|
||||
> STATUS: Works on my machine!
|
||||
|
||||
## Background
|
||||
|
||||
_"The Cricket!"_ by Street Electronics Corporation, released in 1984, is a hardware peripheral for the Apple //c computer. It plugs into the serial port and offers a multi-voice sound synthesizer, a speech synthesizer, and a real-time clock.
|
||||
|
||||
The disks supplied with the device include:
|
||||
* `/CRICKET/PRODOS.MOD` which can be BRUN to patch ProDOS in memory with a clock driver.
|
||||
* A modified version of ProDOS
|
||||
* A utility to patch ProDOS on disk
|
||||
|
||||
## `CRICKET.SYSTEM`
|
||||
|
||||
Like the `NS.CLOCK.SYSTEM` (by "CAP"), `CRICKET.SYSTEM` has these features:
|
||||
|
||||
* [x] A ProDOS `.SYSTEM` file
|
||||
* [x] Detects the presence of a Cricket
|
||||
* [x] Installs a driver in memory following the ProDOS clock driver protocol
|
||||
* [x] Chains to the next `.SYSTEM` file (e.g. `BASIC.SYSTEM`)
|
||||
|
||||
Successfully tested on real hardware. (Laser 128EX, an Apple //c clone — including at 3x speed!)
|
||||
|
||||
## Build
|
||||
|
||||
Requires [cc65](https://github.com/cc65/cc65). The included `Makefile` is very specific to my machine - sorry about that.
|
||||
|
||||
[CRICKET.SYSTEM](cricket.system.s) is the result of the build.
|
||||
|
||||
## Notes
|
||||
|
||||
I ended up disassembling both [NS.CLOCK.SYSTEM](../ns.clock/ns.clock.system.s) (to understand the SYSTEM chaining - what a pain!) and The Cricket!'s [PRODOS.MOD](prodos.mod.s) and melding them together, adding in the detection routine following the protocol in the manual.
|
||||
|
||||
## Other Utilities
|
||||
|
||||
These `BRUN`able files are also built:
|
||||
* [TEST](test.s) attempts to identify an SSC in Slot 2 and the Cricket via the ID sequence, to test routines.
|
||||
* [SET.DATETIME](set.datetime.s) sets the Cricket's current date and time.
|
||||
* [SET.DATE](set.date.s) sets the Cricket's current date.
|
||||
* [SET.TIME](set.time.s) sets the Cricket's current time.
|
||||
|
||||
`CRICKET.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md)
|
||||
|
||||
## Resources
|
||||
|
||||
Cricket disks on Asimov:
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/images/hardware/sound/cricket_disk1.po
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/images/hardware/sound/cricket_disk2.po
|
||||
|
||||
Cricket Manual on Asimov:
|
||||
* ftp://ftp.apple.asimov.net/pub/apple_II/documentation/hardware/sound/Street%20Electronics%20The%20Cricket.pdf
|
||||
|
||||
## FYI...
|
||||
|
||||
In the Cricket manual there is a short sample BASIC program to set the clock without the use of any assembly routines, using `PR#2` to talk to the serial card followed by `PRINT` statements. This does not appear to work from ProDOS with the clock driver in place - the time ends up temporarily scrambled until the clock sorts itself out again. Assembly language routines do work, however. I believe ProDOS attempts to read the clock during the execution of the basic program, which interferes the device.
|
375
clocks/cricket/cricket.system.s
Normal file
375
clocks/cricket/cricket.system.s
Normal file
|
@ -0,0 +1,375 @@
|
|||
;;; The Cricket Clock - ProDOS System
|
||||
;;; Adapted from /CRICKET/PRODOS.MOD
|
||||
;;; Original: Street Electronics Corporation (C) 1984
|
||||
|
||||
;;; Adapted from: /NO.SLOT.CLOCK/NS.CLOCK.SYSTEM
|
||||
;;; Original by "CAP" 04/21/91
|
||||
;;; http://www.apple2.org.za/gswv/a2zine/GS.WorldView/v1999/Oct/MISC/NSC.Disk.TXT
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
read_delay_hi = $3 * 3 ; ($300 iterations is normal * 3.6MHz)
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "Cricket Clock"
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ensure there is not a previous clock driver installed.
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_cricket ; nope, check for Cricket
|
||||
|
||||
rts ; yes, done!
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect Cricket. Detect SSC and if present probe device.
|
||||
|
||||
.proc detect_cricket
|
||||
|
||||
;; Check Slot 2 for SSC. ID bytes per:
|
||||
;; Apple II Technical Note #8: Pascal 1.1 Firmware Protocol ID Bytes
|
||||
lda $C205
|
||||
cmp #$38
|
||||
bne ssc_not_found
|
||||
lda $C207
|
||||
cmp #$18
|
||||
bne ssc_not_found
|
||||
lda $C20B
|
||||
cmp #$01
|
||||
bne ssc_not_found
|
||||
lda $C20C
|
||||
cmp #$31
|
||||
bne ssc_not_found
|
||||
|
||||
beq init_ssc
|
||||
ssc_not_found:
|
||||
jmp not_found
|
||||
|
||||
;; Init SSC and try the "Read Cricket ID code" sequence.
|
||||
init_ssc:
|
||||
php
|
||||
sei
|
||||
lda COMMAND ; save status of SSC registers
|
||||
sta saved_command
|
||||
lda CONTROL
|
||||
sta saved_control
|
||||
|
||||
;; Reset the Cricket (stops any playing notes & ensures Cricket is listening)
|
||||
jsr reset_cricket
|
||||
jsr reset_cricket ; does it twice in original Cricket driver
|
||||
|
||||
;; Read Cricket ID code: 00 ($00)
|
||||
lda #0
|
||||
jsr sendbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
|
||||
;; "The Cricket will return a "C" (195, $C3) followed by a
|
||||
;; version number (in ASCII) and a carriage return (141, $8D)."
|
||||
jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
cmp #HI('C') ; = 'C' ?
|
||||
bne cricket_not_found
|
||||
|
||||
jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
bcc digit
|
||||
|
||||
: jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
cmp #HI(ASCII_CR)
|
||||
beq cricket_found
|
||||
digit: cmp #HI('0') ; < '0' ?
|
||||
bcc cricket_not_found
|
||||
cmp #HI('9' + 1) ; > '9' ?
|
||||
bcs cricket_not_found
|
||||
bcc :-
|
||||
|
||||
cricket_found:
|
||||
jsr restore_cmd_ctl
|
||||
plp
|
||||
jmp install_driver
|
||||
|
||||
cricket_not_found:
|
||||
jsr restore_cmd_ctl
|
||||
plp
|
||||
;; fall through...
|
||||
|
||||
not_found:
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.if ::LOG_FAILURE
|
||||
;; Show failure message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
sec ; failure
|
||||
rts
|
||||
|
||||
reset_cricket:
|
||||
;; Reset SSC
|
||||
lda #0
|
||||
sta COMMAND ; hardware reset all Port 2 ACIA registers
|
||||
sta CONTROL
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
jsr readbyte ; done on original disk
|
||||
lda #HI(ASCII_CR) ; two carriage returns
|
||||
jsr sendbyte
|
||||
lda #HI(ASCII_CR)
|
||||
jsr sendbyte
|
||||
lda #HI('!') ; Reset Cricket (everything but time/date)
|
||||
jsr sendbyte
|
||||
rts
|
||||
|
||||
restore_cmd_ctl:
|
||||
lda saved_control
|
||||
sta CONTROL
|
||||
lda saved_command
|
||||
sta COMMAND
|
||||
rts
|
||||
|
||||
saved_command: .byte 0
|
||||
saved_control: .byte 0
|
||||
.endproc
|
||||
|
||||
;; Write byte in A
|
||||
.proc sendbyte
|
||||
tries := $100 * read_delay_hi
|
||||
counter := $A5
|
||||
|
||||
pha
|
||||
|
||||
lda #<tries
|
||||
sta counter
|
||||
lda #>tries
|
||||
sta counter+1
|
||||
|
||||
check: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
bne ready ; yes, ready to write
|
||||
|
||||
dec counter
|
||||
bne check
|
||||
dec counter+1
|
||||
bne check
|
||||
|
||||
pla
|
||||
sec ; failed
|
||||
rts
|
||||
|
||||
ready:
|
||||
pla
|
||||
sta TDREG
|
||||
clc
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;; Read byte into A, or carry set if timed out
|
||||
.proc readbyte
|
||||
tries := $100 * read_delay_hi
|
||||
counter := $A5
|
||||
|
||||
lda #<tries
|
||||
sta counter
|
||||
lda #>tries
|
||||
sta counter+1
|
||||
|
||||
check: lda STATUS ; did we get it?
|
||||
and #(1 << 3) ; receive register full? (bit 3)
|
||||
bne ready ; yes, we read the value
|
||||
|
||||
dec counter
|
||||
bne check
|
||||
dec counter+1
|
||||
bne check
|
||||
|
||||
sec ; failed
|
||||
rts
|
||||
|
||||
ready: lda RDREG ; actually read the register
|
||||
clc
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install Cricket Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #sizeof_driver-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
lda ROMIN2
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
clc ; success
|
||||
rts ; done!
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Cricket Clock Driver - copied into ProDOS
|
||||
;;; ============================================================
|
||||
|
||||
.proc driver
|
||||
scratch := $3A ; ZP scratch location
|
||||
|
||||
;; Initialize
|
||||
php
|
||||
sei
|
||||
lda COMMAND ; save status of command register
|
||||
pha
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Send command
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
lda #HI('@') ; '@' command
|
||||
sta TDREG
|
||||
|
||||
read_len := 7 ; read 7 bytes (w/m/d/y/H/M/S)
|
||||
|
||||
;; Read response, pushing to stack
|
||||
ldy #(read_len-1)
|
||||
|
||||
rloop: ldx #0 ; x = retry loop counter low byte
|
||||
lda #read_delay_hi ; scratch = retry loop counter high byte
|
||||
sta scratch
|
||||
|
||||
check: lda STATUS ; did we get it?
|
||||
and #(1 << 3) ; receive register full? (bit 3)
|
||||
bne ready ; yes, we read the value
|
||||
|
||||
inx ; not yet, so keep trying
|
||||
bne check ; until counter runs out
|
||||
dec scratch
|
||||
bne check
|
||||
|
||||
;; Read failed - restore stack and exit
|
||||
reset: cpy #(read_len-1) ; anything left to restore?
|
||||
beq done ; nope, exit
|
||||
pla ; yep, clear it off the stack
|
||||
iny
|
||||
bne reset
|
||||
|
||||
;; Read succeeded - stuff it on the stack and continue
|
||||
ready: lda RDREG
|
||||
pha
|
||||
dey
|
||||
bpl rloop
|
||||
|
||||
;; Convert pushed response to ProDOS time field
|
||||
pla ; day of week (unused)
|
||||
|
||||
pla ; minute
|
||||
sta TIMELO ; -- stored as-is (TIMELO 5-0)
|
||||
|
||||
pla ; hour
|
||||
sta TIMELO+1 ; -- stored as-is (TIMELO 12-8)
|
||||
|
||||
pla ; year
|
||||
sta DATELO+1 ; -- will be shifted up by 1 (DATELO 15-9)
|
||||
|
||||
pla ; day
|
||||
and #%00011111 ; -- masked, stored as is (DATELO 4-0)
|
||||
sta DATELO
|
||||
|
||||
pla ; month
|
||||
asl a ; -- shifted up (DATELO 8-5)
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
ora DATELO ; -- merge low 5 bits
|
||||
sta DATELO
|
||||
rol DATELO+1
|
||||
|
||||
pla ; seconds (unused)
|
||||
|
||||
;; Restore prior state
|
||||
done: pla ; restore saved command state
|
||||
sta COMMAND
|
||||
plp
|
||||
rts
|
||||
.endproc
|
||||
sizeof_driver := .sizeof(driver)
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
|
@ -6,7 +6,8 @@
|
|||
.include "apple2.inc"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "./common.inc"
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
|
||||
.org $300
|
||||
|
100
clocks/cricket/set.date.s
Normal file
100
clocks/cricket/set.date.s
Normal file
|
@ -0,0 +1,100 @@
|
|||
;;; SET.DATE utility for The Cricket!
|
||||
;;; Prompts for date and sends Cricket "Set Date" sequence.
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
.org $2000
|
||||
|
||||
.proc main
|
||||
jsr zstrout
|
||||
scrcode "\rDate: WWW MM/DD/YY\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
php
|
||||
sei
|
||||
|
||||
;; Reset SSC
|
||||
sta KBDSTRB ; Port 2 DSR line connected to KBDSTRB
|
||||
lda #0
|
||||
sta COMMAND
|
||||
sta CONTROL
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Clock Commands
|
||||
;; Set Date "SD WWW MM/DD/YY"
|
||||
lda #HI('S')
|
||||
jsr sendbyte
|
||||
lda #HI('D')
|
||||
jsr sendbyte
|
||||
lda #HI(' ')
|
||||
jsr sendbyte
|
||||
|
||||
ldx #0
|
||||
loop: lda INPUT_BUFFER,x
|
||||
jsr sendbyte
|
||||
inx
|
||||
cmp #HI(ASCII_CR)
|
||||
bne loop
|
||||
|
||||
plp
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;; Write byte in A to SSC
|
||||
.proc sendbyte
|
||||
pha
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
pla
|
||||
sta TDREG
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
144
clocks/cricket/set.datetime.s
Normal file
144
clocks/cricket/set.datetime.s
Normal file
|
@ -0,0 +1,144 @@
|
|||
;;; SET.DATETIME utility for The Cricket!
|
||||
;;; Prompts for date and time and updates Cricket.
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
.org $2000
|
||||
|
||||
.proc main
|
||||
;; --------------------------------------------------
|
||||
;; Date
|
||||
|
||||
jsr zstrout
|
||||
scrcode "\rDate: WWW MM/DD/YY\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
php
|
||||
sei
|
||||
|
||||
;; Reset SSC
|
||||
sta KBDSTRB ; Port 2 DSR line connected to KBDSTRB
|
||||
lda #0
|
||||
sta COMMAND
|
||||
sta CONTROL
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Clock Commands
|
||||
;; Set Date "SD WWW MM/DD/YY"
|
||||
lda #HI('S')
|
||||
jsr sendbyte
|
||||
lda #HI('D')
|
||||
jsr sendbyte
|
||||
lda #HI(' ')
|
||||
jsr sendbyte
|
||||
|
||||
ldx #0
|
||||
: lda INPUT_BUFFER,x
|
||||
jsr sendbyte
|
||||
inx
|
||||
cmp #HI(ASCII_CR)
|
||||
bne :-
|
||||
|
||||
plp
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Time
|
||||
|
||||
jsr zstrout
|
||||
scrcode "\rTime: HH:MM:SS\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
php
|
||||
sei
|
||||
|
||||
;; Reset SSC
|
||||
sta KBDSTRB ; Port 2 DSR line connected to KBDSTRB
|
||||
lda #0
|
||||
sta COMMAND
|
||||
sta CONTROL
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Clock Commands
|
||||
;; Set Time "ST HH:MM:SS"
|
||||
lda #HI('S')
|
||||
jsr sendbyte
|
||||
lda #HI('T')
|
||||
jsr sendbyte
|
||||
lda #HI(' ')
|
||||
jsr sendbyte
|
||||
|
||||
ldx #0
|
||||
: lda INPUT_BUFFER,x
|
||||
jsr sendbyte
|
||||
inx
|
||||
cmp #HI(ASCII_CR)
|
||||
bne :-
|
||||
|
||||
plp
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;; Write byte in A to SSC
|
||||
.proc sendbyte
|
||||
pha
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
pla
|
||||
sta TDREG
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
100
clocks/cricket/set.time.s
Normal file
100
clocks/cricket/set.time.s
Normal file
|
@ -0,0 +1,100 @@
|
|||
;;; SET.TIME utility for The Cricket!
|
||||
;;; Prompts for time and sends Cricket "Set Time" sequence.
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
.org $2000
|
||||
|
||||
.proc main
|
||||
jsr zstrout
|
||||
scrcode "\rTime: HH:MM:SS\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
php
|
||||
sei
|
||||
|
||||
;; Reset SSC
|
||||
sta KBDSTRB ; Port 2 DSR line connected to KBDSTRB
|
||||
lda #0
|
||||
sta COMMAND
|
||||
sta CONTROL
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Clock Commands
|
||||
;; Set Time "ST HH:MM:SS"
|
||||
lda #HI('S')
|
||||
jsr sendbyte
|
||||
lda #HI('T')
|
||||
jsr sendbyte
|
||||
lda #HI(' ')
|
||||
jsr sendbyte
|
||||
|
||||
ldx #0
|
||||
loop: lda INPUT_BUFFER,x
|
||||
jsr sendbyte
|
||||
inx
|
||||
cmp #HI(ASCII_CR)
|
||||
bne loop
|
||||
|
||||
plp
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;; Write byte in A to SSC
|
||||
.proc sendbyte
|
||||
pha
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
pla
|
||||
sta TDREG
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
|
@ -1,11 +1,19 @@
|
|||
;;; Test program for The Cricket!
|
||||
;;; * Probes Slot 2 for Super Serial Card (or compatible)
|
||||
;;; * Initializes SSC
|
||||
;;; * Sends Cricket ID sequence
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "./common.inc"
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
.org $2000
|
||||
|
||||
|
@ -28,16 +36,20 @@
|
|||
bne ssc_not_found
|
||||
|
||||
jsr zstrout
|
||||
HIASCIIZ "SSC found.", CR
|
||||
scrcode "SSC found.\r"
|
||||
.byte 0
|
||||
|
||||
jmp init_ssc
|
||||
|
||||
ssc_not_found:
|
||||
jsr zstrout
|
||||
HIASCIIZ "SSC not found.", CR
|
||||
scrcode "SSC not found.\r"
|
||||
.byte 0
|
||||
|
||||
rts
|
||||
|
||||
;; TODO: Write NUL and check for 'C' ... version ... $8D (CR)
|
||||
;; https://github.com/inexorabletash/cricket/issues/3
|
||||
;; https://github.com/a2stuff/cricket/issues/3
|
||||
init_ssc:
|
||||
lda COMMAND ; save status of SSC registers
|
||||
sta saved_command
|
||||
|
@ -59,7 +71,8 @@ init_ssc:
|
|||
;; by a version number (in ASCII) and a carriage return (141,
|
||||
;; $8D)."
|
||||
jsr zstrout
|
||||
HIASCIIZ "Reading SSC: "
|
||||
scrcode "Reading SSC: "
|
||||
.byte 0
|
||||
|
||||
jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
|
@ -72,7 +85,7 @@ init_ssc:
|
|||
|
||||
: jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
cmp #HI(CR) ; = CR ?
|
||||
cmp #HI(ASCII_CR)
|
||||
beq cricket_found
|
||||
digit: cmp #HI('0') ; < '0' ?
|
||||
bcc cricket_not_found
|
||||
|
@ -84,12 +97,14 @@ digit: cmp #HI('0') ; < '0' ?
|
|||
|
||||
cricket_found:
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, "Cricket tentatively found.", CR
|
||||
scrcode "\rCricket tentatively found.\r"
|
||||
.byte 0
|
||||
jmp exit
|
||||
|
||||
cricket_not_found:
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, "Cricket not identified.", CR
|
||||
scrcode "\rCricket not identified.\r"
|
||||
.byte 0
|
||||
jmp exit
|
||||
|
||||
exit:
|
||||
|
@ -138,7 +153,8 @@ check: lda STATUS ; did we get it?
|
|||
bne check
|
||||
|
||||
jsr zstrout
|
||||
HIASCIIZ "... timeout!"
|
||||
scrcode "... timeout!"
|
||||
.byte 0
|
||||
|
||||
sec ; failed
|
||||
rts
|
46
clocks/dclock/Makefile
Normal file
46
clocks/dclock/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/dclock.system.SYS \
|
||||
$(OUTDIR)/dclock.setup.SYS
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
15
clocks/dclock/README.md
Normal file
15
clocks/dclock/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Applied Engineering DClock — ProDOS Clock Driver
|
||||
|
||||
This is based on a disassembly of the Applied Engineering driver for the DClock real time clock add on for the Apple IIc.
|
||||
|
||||
> NOTE: Currently untested!
|
||||
|
||||
Like other drivers here, this one will:
|
||||
|
||||
* Conditionally install, only if a DClock is detected.
|
||||
* Only attempts detection if there is not already a clock driver.
|
||||
* Only attempts detection if the system is a an Apple IIc
|
||||
* If detected, installs into ProDOS directly, following Technical Reference Manual requirements.
|
||||
* Chains to the next `.SYSTEM` file in the directory.
|
||||
|
||||
`DCLOCK.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md).
|
406
clocks/dclock/dclock.system.s
Normal file
406
clocks/dclock/dclock.system.s
Normal file
|
@ -0,0 +1,406 @@
|
|||
; Fully disassembled and analyzed source to AE
|
||||
; DCLOCK.SYSTEM by M.G. - 04/18/2017
|
||||
; https://gist.github.com/mgcaret/7f0d7aeec169e90809c7cfaab9bf183b
|
||||
; Further modified by @inexorabletash - 12/21/2020
|
||||
|
||||
; There are critical bugs in the original AE code:
|
||||
; * When driver loader is initially probing it corrupts the
|
||||
; Apple //c Memory Expansion Card:
|
||||
; - it saves, but fails to restore, data at address $080000
|
||||
; - it fails to reset slinky pointer, and *will* trash $080000-$080007
|
||||
; * When the clock is read, it corrupts data at address $08xx01
|
||||
; - John Brooks spotted this, [M.G.] totally missed this.
|
||||
; This version of the code has fixes permanently applied.
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.setcpu "6502" ; changed below
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
; zero page locations
|
||||
SCRATCH := $0B ; scratch value for BCD range checks
|
||||
SAVEBYTE := $0C ; slinky overwritten byte save location
|
||||
BCDTMP := $3A ; location clock driver uses for BCD->Binary
|
||||
|
||||
; buffers & other spaces
|
||||
INBUF := $0200 ; input buffer
|
||||
CLOCKBUF := $0300 ; clock buffer
|
||||
|
||||
; I/O and hardware
|
||||
C8OFF := $CFFF ; C8xx ROM off
|
||||
SLOT4ROM := $C400 ; Slot 4 ROM space
|
||||
SLOT4IO := $C0C0 ; Slot 4 I/O space
|
||||
DPTRL := SLOT4IO+0 ; Slinky data ptr low
|
||||
DPTRM := SLOT4IO+1 ; Slinky data ptr middle
|
||||
DPTRH := SLOT4IO+2 ; Slinky data ptr high
|
||||
DATA := SLOT4IO+3 ; Slinky data byte
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "DClock"
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ensure there is not a previous clock driver installed.
|
||||
;;; And that this is a IIc. And that the clock is present.
|
||||
;;; NOTE: Safe to run on a non-IIc (6502 opcodes, etc)
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
bne done
|
||||
|
||||
lda VERSION ; IIc identification byte 1
|
||||
cmp #$06
|
||||
bne done
|
||||
lda ZIDBYTE ; IIc identification byte 2
|
||||
cmp #$00
|
||||
bne done
|
||||
|
||||
;;; Since this is a IIc, okay to rely on 65C02 opcodes from here.
|
||||
.pushcpu
|
||||
.setcpu "65C02"
|
||||
|
||||
jsr ClockRead
|
||||
jsr ValidTime
|
||||
bcc InstallDriver
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.if ::LOG_FAILURE
|
||||
;; Show failure message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
done: sec ; failure
|
||||
rts
|
||||
.endproc
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; Install clock driver
|
||||
.proc InstallDriver
|
||||
|
||||
;; Copy into address at DATETIME vector, update the vector and
|
||||
;; update MACHID bits to signal a clock is present.
|
||||
ptr := $A5
|
||||
|
||||
;; Update absolute addresses within driver
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
clc
|
||||
adc #(regulk - driver)
|
||||
sta regulk_addr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
adc #0
|
||||
sta regulk_addr+1
|
||||
|
||||
;; Copy driver into appropriate bank
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #sizeof_driver-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
lda ROMIN2
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
clc ; success
|
||||
rts ; done!
|
||||
.endproc
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; enable slinky registers, set adddress and save byte we intend to trash
|
||||
.proc SlinkyEnable
|
||||
lda C8OFF ; not needed on //c, but release $C8xx firmware
|
||||
lda SLOT4ROM ; enable slinky registers
|
||||
lda #$08 ; set addr $080000
|
||||
sta DPTRH
|
||||
stz DPTRM
|
||||
stz DPTRL
|
||||
lda DATA ; read data byte
|
||||
sta SAVEBYTE ; save it to restore later
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; Routine to restore trashed byte in slinky RAM
|
||||
.proc SlinkyRestore
|
||||
lda #$08 ; set adddr $080000
|
||||
sta DPTRH
|
||||
stz DPTRM
|
||||
stz DPTRL
|
||||
lda SAVEBYTE ; get saved byte
|
||||
sta DATA ; and put it back
|
||||
lda C8OFF ; not needed on //c, but release $C8xx firmware
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; Write 8 bits to clock
|
||||
.proc ClockWrite8b
|
||||
ldx #$08 ; set adddr $080000
|
||||
stx DPTRH
|
||||
stz DPTRM
|
||||
: stz DPTRL ; restore low byte to 0
|
||||
sta DATA ; write byte
|
||||
lsr a ; next bit into 0 position
|
||||
dex
|
||||
bne :-
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; unlock the clock by writing the magic bit sequence
|
||||
.proc ClockUnlock
|
||||
ldy #$08
|
||||
: lda unlock,y
|
||||
jsr ClockWrite8b ; write 8 bits
|
||||
dey
|
||||
bne :-
|
||||
rts
|
||||
unlock = * - 1
|
||||
.byte $5c, $a3, $3a, $c5, $5c, $a3, $3a, $c5
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; Read 8 bits from the clock
|
||||
.proc ClockRead8b
|
||||
ldx #$08 ; set adddr $080000
|
||||
stz DPTRL
|
||||
stz DPTRM
|
||||
stx DPTRH
|
||||
: pha ; save accumulator
|
||||
lda DATA ; get data byte
|
||||
lsr a ; bit 0 into carry
|
||||
pla ; restore accumulator
|
||||
ror a ; put read bit into position
|
||||
dex
|
||||
bne :-
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; read the clock data into memory at CLOCKBUF
|
||||
; WARNING: unfixed code never restores byte we trashed
|
||||
.proc ClockRead
|
||||
jsr SlinkyEnable
|
||||
jsr ClockUnlock
|
||||
ldy #$00
|
||||
: jsr ClockRead8b
|
||||
sta CLOCKBUF,y
|
||||
iny
|
||||
cpy #$08 ; have we read 8 bytes?
|
||||
bcc :- ; nope
|
||||
jsr SlinkyRestore
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; validate the DClock data makes sense
|
||||
; return carry clear if it does, carry set if it does not
|
||||
.proc ValidTime
|
||||
; validate ms
|
||||
ldx #$00
|
||||
ldy #$99
|
||||
lda CLOCKBUF
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate seconds
|
||||
ldx #$00
|
||||
ldy #$59
|
||||
lda CLOCKBUF+$01
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate minutes
|
||||
ldx #$00
|
||||
ldy #$59
|
||||
lda CLOCKBUF+$02
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate hours
|
||||
ldx #$00
|
||||
ldy #$23
|
||||
lda CLOCKBUF+$03
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate day of week
|
||||
ldx #$01
|
||||
ldy #$07
|
||||
lda CLOCKBUF+$04
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate day of month
|
||||
ldx #$01
|
||||
ldy #$31
|
||||
lda CLOCKBUF+$05
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate month
|
||||
ldx #$01
|
||||
ldy #$12
|
||||
lda CLOCKBUF+$06
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
; validate year
|
||||
ldx #$00
|
||||
ldy #$99
|
||||
lda CLOCKBUF+$07
|
||||
jsr CheckBCD
|
||||
bcs :+
|
||||
clc ; all good
|
||||
rts
|
||||
: sec ; problem
|
||||
rts
|
||||
.endproc
|
||||
; ----------------------------------------------------------------------------
|
||||
; Check BCD number in range of [x,y]
|
||||
; return carry clear if it is, carry set if it is not
|
||||
.proc CheckBCD
|
||||
sed ; decimal mode
|
||||
stx SCRATCH ; lower bound into scratch
|
||||
cmp SCRATCH ; compare it
|
||||
bcc :++ ; fail if out of range
|
||||
sty SCRATCH ; upper bound into scratch
|
||||
cmp SCRATCH ; compare it
|
||||
beq :+ ; OK if equal
|
||||
bcs :++ ; fail if out of range
|
||||
: cld ; in range
|
||||
clc
|
||||
rts
|
||||
: cld ; not in range
|
||||
sec
|
||||
rts
|
||||
.endproc
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; clock driver code inserted into ProDOS
|
||||
|
||||
driver:
|
||||
lda #$08 ; useless instruction
|
||||
php
|
||||
sei
|
||||
lda SLOT4ROM ; activate slinky registers
|
||||
; ($08 from above overwritten)
|
||||
stz DPTRL ; set slinky address to $08xx00
|
||||
ldy #$08 ; also counter for unlock bytes
|
||||
sty DPTRH
|
||||
lda DATA ; get destroyed byte
|
||||
; (slinky now at $08xx01)
|
||||
pha ; save value on stack
|
||||
; unlock dclock registers
|
||||
regulk_addr := *+1
|
||||
ubytlp: lda regulk,y ; self-modified
|
||||
ldx #$08 ; bit counter
|
||||
ubitlp: stz DPTRL ; reset pointer to $08xx00
|
||||
sta DATA ; write to $08xx00
|
||||
lsr a ; next bit into 0 position
|
||||
dex
|
||||
bne ubitlp
|
||||
dey
|
||||
bne ubytlp
|
||||
; now read 64 bits (8 bytes) from dclock
|
||||
ldx #$08 ; byte counter
|
||||
rbytlp: ldy #$08 ; bit counter
|
||||
rbitlp: pha
|
||||
lda DATA ; data byte
|
||||
lsr a ; bit 0 into carry
|
||||
pla
|
||||
ror a ; carry into bit 7
|
||||
dey
|
||||
bne rbitlp
|
||||
; got 8 bits now, convert from BCD to binary
|
||||
pha
|
||||
and #$0F
|
||||
sta BCDTMP
|
||||
pla
|
||||
and #$F0
|
||||
lsr a
|
||||
pha
|
||||
adc BCDTMP
|
||||
sta BCDTMP
|
||||
pla
|
||||
lsr a
|
||||
lsr a
|
||||
adc BCDTMP
|
||||
; place in input buffer, which is OK because the ThunderClock driver does this
|
||||
sta INBUF-1,x
|
||||
dex
|
||||
bne rbytlp
|
||||
; done copying, now put necessary values into ProDOS time locations
|
||||
; copy hours to ProDOS hours
|
||||
lda INBUF+4
|
||||
sta TIMEHI
|
||||
; copy minutes to ProDOS minutes
|
||||
lda INBUF+5
|
||||
sta TIMELO
|
||||
; copy month ...
|
||||
lda INBUF+1
|
||||
lsr a
|
||||
ror a
|
||||
ror a
|
||||
ror a
|
||||
; ... and day of month to ProDOS month/day
|
||||
ora INBUF+2
|
||||
sta DATELO
|
||||
; copy year and final bit of month to ProDOS year/month
|
||||
lda INBUF
|
||||
rol a
|
||||
sta DATEHI
|
||||
stz DPTRL ; set slinky back to $08xx00
|
||||
pla ; get saved byte
|
||||
sta DATA ; put it back
|
||||
plp
|
||||
rts
|
||||
; DS1215 unlock sequence (in reverse)
|
||||
regulk = * - 1
|
||||
.byte $5C, $A3, $3A, $C5, $5C, $A3, $3A, $C5
|
||||
|
||||
sizeof_driver := * - driver
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
.popcpu
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
46
clocks/fujinet/Makefile
Normal file
46
clocks/fujinet/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/fn.clock.system.SYS \
|
||||
$(OUTDIR)/fn.clock.setup.SYS
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.BIN $(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
7
clocks/fujinet/README.md
Normal file
7
clocks/fujinet/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# FujiNet ProDOS Clock Driver
|
||||
|
||||
[FujiNet](https://fujinet.online/) for Apple II provides a number of devices via SmartPort. Alongside four block and a network device it has a real time clock device.
|
||||
|
||||
This driver is an adaptation of the other drivers in https://github.com/a2stuff/prodos-drivers
|
||||
|
||||
`FN.CLOCK.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md).
|
201
clocks/fujinet/fn.clock.system.s
Normal file
201
clocks/fujinet/fn.clock.system.s
Normal file
|
@ -0,0 +1,201 @@
|
|||
;;; ProDOS driver for the Fujinet clock
|
||||
;;; Adapted from: https://github.com/a2stuff/prodos-drivers/blob/main/cricket/cricket.system.s
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
||||
|
||||
.include "./smartport.inc"
|
||||
|
||||
FN_CLOCK_DEVICE_TYPE := $13 ; As defined on the Fujinet firmware
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "Fujinet Clock"
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ensure there is not a previous clock driver installed.
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_fujinet_clock ; nope, check for clock
|
||||
|
||||
rts ; yes, done!
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Fujinet Clock Driver - copied into ProDOS
|
||||
;;; ============================================================
|
||||
|
||||
.proc driver
|
||||
scratch := $3A ; ZP scratch location
|
||||
|
||||
;; Initialize
|
||||
php
|
||||
sei
|
||||
|
||||
;; Execute smartport command
|
||||
jsr $c50d ; To be changed to the detected slot and address
|
||||
drv_call_hi = *-1
|
||||
drv_call_lo = *-2
|
||||
.byte DRIVER_COMMAND_STATUS ; Command Status
|
||||
params_address:
|
||||
.word params - driver ; To be changed on relocation
|
||||
|
||||
;; Restore state and return
|
||||
sta $CFFF ; release C8xx ROM space
|
||||
plp
|
||||
rts
|
||||
|
||||
params: .byte $03 ; Status param count
|
||||
port: .byte $00 ; Smartport device
|
||||
.word DATELO ; Write directly on the four bytes reserved by Prodos for date and time
|
||||
.byte 'P' ; Get datetime in ProDDOS format
|
||||
|
||||
.endproc
|
||||
.assert .sizeof(driver) <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect Fujinet Clock.
|
||||
|
||||
.proc detect_fujinet_clock
|
||||
|
||||
;; Search for smartport cards
|
||||
ldx #$C7 ; Start the search from slot 7
|
||||
search_slot:
|
||||
jsr find_smartport
|
||||
bcs not_found
|
||||
|
||||
;; Find a Fujinet Clock device on this slot
|
||||
jsr setup_smartport
|
||||
jsr device_count
|
||||
cpx #$0
|
||||
beq continue_slot_search; no devices in the slot
|
||||
|
||||
search_unit:
|
||||
jsr unit_type
|
||||
cmp #FN_CLOCK_DEVICE_TYPE
|
||||
beq found
|
||||
dex
|
||||
bne search_unit
|
||||
continue_slot_search:
|
||||
ldx sp_call_hi ; restore card
|
||||
dex
|
||||
cpx #$C0
|
||||
bne search_slot
|
||||
jmp not_found
|
||||
found:
|
||||
; Modify the driver code with the detected data
|
||||
stx driver::port
|
||||
lda sp_call_lo
|
||||
sta driver::drv_call_lo
|
||||
lda sp_call_hi
|
||||
sta driver::drv_call_hi
|
||||
|
||||
jmp install_driver
|
||||
|
||||
not_found:
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.if ::LOG_FAILURE
|
||||
;; Show failure message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
sec ; failure
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
;; Find driver destination
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
|
||||
;; Fix pointers
|
||||
clc
|
||||
lda ptr
|
||||
adc driver::params_address
|
||||
sta driver::params_address
|
||||
lda ptr+1
|
||||
adc driver::params_address+1
|
||||
sta driver::params_address+1
|
||||
|
||||
;; Copy code
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #.sizeof(driver)-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
lda ROMIN2
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
clc ; success
|
||||
rts ; done!
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
111
clocks/fujinet/smartport.inc
Normal file
111
clocks/fujinet/smartport.inc
Normal file
|
@ -0,0 +1,111 @@
|
|||
;;; ------------------------------------------------------------
|
||||
;;; Smartport access functions
|
||||
;;; Derived from: http://mirrors.apple2.org.za/ground.icaen.uiowa.edu/MiscInfo/Programming/smartport.statusexample
|
||||
|
||||
;;; This function scans the slots to locate a SmartPort.
|
||||
;;; On entry, X=$Cx, where x is the first slot to be checked.
|
||||
;;; On exit, X=$Cy, where y is the highest numbered slot less than or
|
||||
;;; equal to x which contains SmartPort firmware. If no SmartPort
|
||||
;;; is found, C=1 and A=$00.
|
||||
ptr := $A5 ; Generic pointer
|
||||
|
||||
.proc find_smartport
|
||||
LDA #$00
|
||||
STA ptr ; Set up the pointer
|
||||
try_slot:
|
||||
STX ptr+1
|
||||
LDY #$01
|
||||
LDA (ptr),Y ; Check the first ID byte
|
||||
CMP #$20
|
||||
BNE not_here
|
||||
LDY #$03
|
||||
LDA (ptr),Y ; and the second one
|
||||
CMP #$00
|
||||
BNE not_here
|
||||
LDY #$05
|
||||
LDA (ptr),Y ; and the third one
|
||||
CMP #$03
|
||||
BNE not_here
|
||||
LDY #$07
|
||||
LDA (ptr),Y ; and the fourth one
|
||||
CMP #$00
|
||||
BNE not_here
|
||||
LDX ptr+1 ; Match! Get the address back
|
||||
CLC
|
||||
RTS
|
||||
not_here:
|
||||
LDX ptr+1 ; Mismatch
|
||||
DEX ; Go down one slot
|
||||
CPX #$C1
|
||||
BCS try_slot ; Stop once we have gone past slot 1
|
||||
LDX #$00
|
||||
SEC ; Error - no SmartPort found
|
||||
RTS
|
||||
.endproc
|
||||
|
||||
;;; This function sets up the SP_CALL function for calling the
|
||||
;;; SmartPort driver. On entry, X=$Cx, where x is the slot number
|
||||
;;; containing a SmartPort driver. This should be checked via
|
||||
;;; FIND_SMARTPORT if necessary - don't assume there is a SmartPort
|
||||
;;; device in slot 5, for example!
|
||||
.proc setup_smartport
|
||||
LDA #$00
|
||||
STA ptr ; Set up the pointer
|
||||
STX ptr+1
|
||||
LDY #$FF
|
||||
LDA (ptr),Y ; Get the ProDOS driver entry point
|
||||
CLC
|
||||
ADC #$03 ; Get the SmartPort driver entry point
|
||||
STA sp_call_lo ; Store in the JSR
|
||||
STX sp_call_hi ; also store the high byte
|
||||
RTS
|
||||
.endproc
|
||||
|
||||
;;; This function return in X the number of devices available
|
||||
;;; on a SmartPort
|
||||
.proc device_count
|
||||
LDA #$00
|
||||
STA st_unit
|
||||
STA st_code
|
||||
JSR sp_call
|
||||
BCS device_count_error
|
||||
LDX st_list+0
|
||||
RTS
|
||||
device_count_error:
|
||||
LDX #$00
|
||||
RTS
|
||||
.endproc
|
||||
|
||||
;;; This function returns in A the device type for a unit in X
|
||||
.proc unit_type
|
||||
STX st_unit
|
||||
LDA #$03
|
||||
STA st_code
|
||||
JSR sp_call
|
||||
BCS unit_type_error
|
||||
LDA st_list+21
|
||||
LDX st_unit
|
||||
RTS
|
||||
unit_type_error:
|
||||
LDA #$ff
|
||||
LDX st_unit
|
||||
RTS
|
||||
.endproc
|
||||
|
||||
|
||||
|
||||
;;; Status command parameters
|
||||
sp_call: JSR $0000
|
||||
sp_call_hi = *-1
|
||||
sp_call_lo = *-2
|
||||
.byte DRIVER_COMMAND_STATUS ; Command Status
|
||||
params_address:
|
||||
.word st_params
|
||||
RTS
|
||||
|
||||
st_params:
|
||||
.byte $3 ; Parameter count
|
||||
st_unit:.byte $0
|
||||
.word st_list
|
||||
st_code:.byte $0
|
||||
st_list:.byte 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24
|
52
clocks/jumbo/Makefile
Normal file
52
clocks/jumbo/Makefile
Normal file
|
@ -0,0 +1,52 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc) \
|
||||
../ns.clock/ns.clock.system.s \
|
||||
../romx/romxrtc.system.s \
|
||||
../dclock/dclock.system.s \
|
||||
../fujinet/fn.clock.system.s \
|
||||
../fujinet/smartport.inc \
|
||||
../cricket/cricket.system.s
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/clock.system.SYS \
|
||||
$(OUTDIR)/clock.setup.SYS
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
17
clocks/jumbo/README.md
Normal file
17
clocks/jumbo/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# "Jumbo" ProDOS Clock Driver
|
||||
|
||||
This is an amalgamation of the other clock driver installers. Each one is tried in turn, until one successfully installs a clock driver.
|
||||
|
||||
The drivers are (in order):
|
||||
|
||||
* No-Slot Clock
|
||||
* ROMX
|
||||
* FujiNet
|
||||
* DClock
|
||||
* Cricket!
|
||||
|
||||
By default, the installer logs on success so you can tell what clock was detected, but you can build with `LOG_SUCCESS=0` to prevent that.
|
||||
|
||||
If ProDOS _already_ has a clock driver installed, the driver is checked for common Thunderclock year tables. If found, the table is updated in memory to cover 2023-2028.
|
||||
|
||||
`CLOCK.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md).
|
161
clocks/jumbo/clock.system.s
Normal file
161
clocks/jumbo/clock.system.s
Normal file
|
@ -0,0 +1,161 @@
|
|||
;;; "Jumbo" Clock Driver
|
||||
;;;
|
||||
;;; Pulls in several clock drivers sources and tries each one in sequence.
|
||||
;;;
|
||||
|
||||
JUMBO_CLOCK_DRIVER = 1
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.include "../../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.scope ns_clock
|
||||
.include "../ns.clock/ns.clock.system.s"
|
||||
.endscope
|
||||
.scope romx
|
||||
.include "../romx/romxrtc.system.s"
|
||||
.endscope
|
||||
.scope dclock
|
||||
.include "../dclock/dclock.system.s"
|
||||
.endscope
|
||||
.scope fujinet
|
||||
.include "../fujinet/fn.clock.system.s"
|
||||
.endscope
|
||||
.scope cricket
|
||||
.include "../cricket/cricket.system.s"
|
||||
.endscope
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "Jumbo Clock Driver"
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
bne check_thunderclock
|
||||
|
||||
jsr ns_clock::maybe_install_driver
|
||||
bcc ret
|
||||
|
||||
jsr romx::maybe_install_driver
|
||||
bcc ret
|
||||
|
||||
jsr dclock::maybe_install_driver
|
||||
bcc ret
|
||||
|
||||
jsr fujinet::maybe_install_driver
|
||||
bcc ret
|
||||
|
||||
jsr cricket::maybe_install_driver
|
||||
bcc ret
|
||||
|
||||
ret: rts
|
||||
.endproc
|
||||
|
||||
.proc check_thunderclock
|
||||
;; Look for Thunderclock year table
|
||||
bit RWRAM1
|
||||
bit RWRAM1
|
||||
|
||||
lda #<table_1982
|
||||
ldx #>table_1982
|
||||
jsr check_sig
|
||||
beq update_table
|
||||
|
||||
lda #<table_1986
|
||||
ldx #>table_1986
|
||||
jsr check_sig
|
||||
beq update_table
|
||||
|
||||
lda #<table_1993
|
||||
ldx #>table_1993
|
||||
jsr check_sig
|
||||
beq update_table
|
||||
|
||||
lda #<table_2018
|
||||
ldx #>table_2018
|
||||
jsr check_sig
|
||||
beq update_table
|
||||
|
||||
;; Table not found - we have a clock but don't
|
||||
;; know what it is, so don't log anything.
|
||||
bit ROMIN2
|
||||
rts
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
;; Copy the latest table into place
|
||||
update_table:
|
||||
ldx #SIG_LEN-1
|
||||
: lda table_2023,x
|
||||
sta SIG_ADDR,x
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
bit ROMIN2
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message, to confirm table updates.
|
||||
jsr log_message
|
||||
scrcode "ThunderClock - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
rts
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
check_sig:
|
||||
ptr := $06
|
||||
sta ptr
|
||||
stx ptr+1
|
||||
ldy #SIG_LEN-1
|
||||
: lda (ptr),y
|
||||
cmp SIG_ADDR,y
|
||||
bne :+ ; Z=0 for no match
|
||||
dey
|
||||
bpl :-
|
||||
iny ; Z=1 for match
|
||||
: rts
|
||||
|
||||
SIG_LEN = 7
|
||||
SIG_ADDR := $D7B8
|
||||
|
||||
table_1982: ; ProDOS 1.1.1
|
||||
.byte $54, $54, $53, $52, $57, $56, $55
|
||||
|
||||
table_1986: ; ProDOS 1.3 - 1.9
|
||||
.byte $5A, $59, $58, $58, $57, $56, $5B
|
||||
|
||||
table_1993: ; ProDOS 2.0.3
|
||||
.byte $60, $5F, $5E, $5D, $62, $61, $60
|
||||
|
||||
table_2018: ; ProDOS 2.4.2
|
||||
.byte $12, $17, $16, $15, $14, $14, $13
|
||||
|
||||
table_2023:
|
||||
.byte $18, $17, $1C, $1B, $1A, $19, $18
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
53
clocks/ns.clock/Makefile
Normal file
53
clocks/ns.clock/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
UTILDIR = $(OUTDIR)/nsclock.util
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/ns.clock.system.SYS \
|
||||
$(OUTDIR)/ns.clock.setup.SYS \
|
||||
$(UTILDIR)/set.datetime.BIN
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(UTILDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR) $(UTILDIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(UTILDIR)/*.o
|
||||
rm -f $(UTILDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o $(UTILDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
|
||||
$(UTILDIR)/%.BIN: $(UTILDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
16
clocks/ns.clock/README.md
Normal file
16
clocks/ns.clock/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# No Slot Clock ProDOS Driver
|
||||
|
||||
Adapted from `NS.CLOCK.SYSTEM` (by "CAP"), with these changes:
|
||||
|
||||
* Fixes a typo
|
||||
* Removes beeps
|
||||
* Is less chatty so you can have multiple clock drivers, e.g. if you use the same hard disk image across different hardware configurations
|
||||
* Uses file I/O rather than block I/O for chaining
|
||||
* Does not hard-code driver file name
|
||||
|
||||
## Other Utilities
|
||||
|
||||
These `BRUN`able files are also built:
|
||||
* [SET.DATETIME](set.datetime.s) sets the No Slot Clock date/time.
|
||||
|
||||
`NS.CLOCK.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md).
|
387
clocks/ns.clock/ns.clock.system.s
Normal file
387
clocks/ns.clock/ns.clock.system.s
Normal file
|
@ -0,0 +1,387 @@
|
|||
;;; NS.CLOCK.SYSTEM
|
||||
;;; Original by "CAP" 04/21/91
|
||||
;;; http://www.apple2.org.za/gswv/a2zine/GS.WorldView/v1999/Oct/MISC/NSC.Disk.TXT
|
||||
|
||||
;;; Modification history available at:
|
||||
;;; https://github.com/a2stuff/prodos-drivers
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "No-Slot Clock"
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ensure there is not a previous clock driver installed.
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_nsc ; nope, check for NSC
|
||||
|
||||
rts ; yes, done!
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect NSC. Scan slot ROMs and main ROMs. Try reading
|
||||
;;; each location several times, and validate results before
|
||||
;;; installing driver.
|
||||
|
||||
.proc detect_nsc
|
||||
;; Preserve date/time
|
||||
ldy #3 ; copy 4 bytes
|
||||
: lda DATELO,y
|
||||
sta saved,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Check slot ROMs
|
||||
lda #>$CFFF
|
||||
ldy #<$CFFF
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #0
|
||||
sta slot
|
||||
lda #3 ; treat slot 0 as slot 3
|
||||
|
||||
sloop: ora #$C0 ; A=$Cs
|
||||
pha
|
||||
jsr DetectZ80
|
||||
pla ; A=$Cs
|
||||
bcs next ; Z80 present, skip this slot
|
||||
|
||||
sta st1+2
|
||||
rloop: sta ld1+2
|
||||
sta ld2+2
|
||||
sta st2+2
|
||||
|
||||
lda #3 ; 3 tries - need valid results each time
|
||||
sta tries
|
||||
try: jsr driver ; try reading date/time
|
||||
lda DATELO+1 ; check result
|
||||
ror a
|
||||
lda DATELO
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #$0F
|
||||
beq next
|
||||
cmp #13 ; month
|
||||
bcs next
|
||||
lda DATELO
|
||||
and #$1F
|
||||
beq next
|
||||
cmp #32 ; day
|
||||
bcs next
|
||||
lda TIMELO+1
|
||||
cmp #24 ; hours
|
||||
bcs next
|
||||
lda TIMELO
|
||||
cmp #60 ; minutes
|
||||
bcs next
|
||||
dec tries
|
||||
bne try
|
||||
beq install_driver ; all tries look valid
|
||||
next: inc slot
|
||||
lda slot
|
||||
cmp #8
|
||||
bcc sloop ; next slot
|
||||
bne not_found
|
||||
|
||||
;; Not found in slot ROM, try main ROMs ???
|
||||
lda #>$C015 ; $C015 = RDCXROM (internal or slot ROM?)
|
||||
ldy #<$C015
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
ldy #$07 ; $C007 = INTCXROM (read internal ROM)
|
||||
sta st1+2
|
||||
sty st1+1
|
||||
dey ; $C006 = SLOTCXROM (read slot ROM)
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #>$C800
|
||||
bne rloop
|
||||
|
||||
;; Restore date/time
|
||||
not_found:
|
||||
ldy #3
|
||||
: lda saved,y
|
||||
sta DATELO,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.if ::LOG_FAILURE
|
||||
;; Show failure message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
sec ; failure
|
||||
rts
|
||||
|
||||
saved: .byte 0, 0, 0, 0
|
||||
tries: .byte 3
|
||||
slot: .byte 0
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install NSC Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
;; Update absolute addresses within driver
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
clc
|
||||
adc #(unlock - driver - 1)
|
||||
sta unlock_addr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
adc #0
|
||||
sta unlock_addr+1
|
||||
|
||||
;; Copy driver into appropriate bank
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #sizeof_driver-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
lda ROMIN2
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
clc ; success
|
||||
rts ; done!
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect Z80
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; This routine gets swapped into $0FFD for execution
|
||||
.proc Z80Routine
|
||||
target := $0FFD
|
||||
;; .org $FFFD
|
||||
patch := *+2
|
||||
.byte $32, $00, $e0 ; ld ($Es00),a ; s=slot being probed turn off Z80, next PC is $0000
|
||||
.byte $3e, $01 ; ld a,$01
|
||||
.byte $32, $08, $00 ; ld (flag),a
|
||||
.byte $c3, $fd, $ff ; jp $FFFD
|
||||
flag := *
|
||||
.byte $00 ; flag: .db $00
|
||||
.endproc
|
||||
.assert Z80Routine > Z80Routine::target + .sizeof(Z80Routine), error, "Z80 collision"
|
||||
|
||||
;;; Input: A = $Cn where n = slot number
|
||||
;;; Output: C=1 if Z80 found in slot
|
||||
.proc DetectZ80
|
||||
;; Location to poke to invoke Z80
|
||||
sta store+1
|
||||
|
||||
;; Convert $Cn to $En, update Z80 code
|
||||
ora #$E0
|
||||
sta Z80Routine::patch
|
||||
|
||||
;; Clear detection flag
|
||||
copy #0, Z80Routine::flag
|
||||
|
||||
;; Put routine in place
|
||||
jsr SwapRoutine
|
||||
|
||||
;; Try to invoke Z80
|
||||
php
|
||||
sei
|
||||
store := *+1
|
||||
sta $C000 ; self-modified
|
||||
plp
|
||||
|
||||
;; Restore memory
|
||||
jsr SwapRoutine
|
||||
|
||||
;; Flag will be set to 1 by routine if Z80 was present.
|
||||
lda Z80Routine::flag
|
||||
ror ; move flag into carry
|
||||
rts
|
||||
|
||||
.proc SwapRoutine
|
||||
ldx #.sizeof(Z80Routine)-1
|
||||
: ldy Z80Routine::target,x
|
||||
lda Z80Routine,x
|
||||
sta Z80Routine::target,x
|
||||
tya
|
||||
sta Z80Routine,x
|
||||
dex
|
||||
bpl :-
|
||||
rts
|
||||
.endproc
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; NSC driver - modified as needed and copied into ProDOS
|
||||
;;; ============================================================
|
||||
|
||||
driver:
|
||||
php
|
||||
sei
|
||||
lda PTRIG ; Slow ZIP, IIc+ accelerator, etc
|
||||
lda $C00B ; Ultrawarp bug workaround c/o @bobbimanners
|
||||
ld4: lda $CFFF ; self-modified ($CFFF or RDCXROM)
|
||||
pha
|
||||
st1: sta $C300 ; self-modified ($Cn00 or INTCXROM)
|
||||
ld1: lda $C304 ; self-modified ($Cn04)
|
||||
ldx #8
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Unlock the NSC by bit-banging.
|
||||
uloop:
|
||||
unlock_addr := *+1
|
||||
lda unlock-1,x ; self-modified (during relocation)
|
||||
sec
|
||||
ror a ; a bit at a time
|
||||
: pha
|
||||
lda #0
|
||||
rol a
|
||||
tay
|
||||
ld2: lda $C300,y ; self-modified ($Cn00)
|
||||
pla
|
||||
lsr a
|
||||
bne :-
|
||||
dex
|
||||
bne uloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Read 8 bytes * 8 bits of clock data, push onto stack
|
||||
|
||||
tmp := $200
|
||||
ldx #8
|
||||
bloop: ldy #8
|
||||
st2:
|
||||
: lda $C304 ; self-modified ($Cn04)
|
||||
ror a
|
||||
ror tmp
|
||||
dey
|
||||
bne :-
|
||||
|
||||
;; BCD to Binary - slow but tiny
|
||||
lda tmp ; A = value
|
||||
ldy #$FF ; result = -1
|
||||
sec
|
||||
sed
|
||||
: iny ; result += 1
|
||||
sbc #1 ; value -= 1
|
||||
bcs :-
|
||||
cld
|
||||
tya ; A = result
|
||||
|
||||
;; Push to stack
|
||||
pha
|
||||
dex
|
||||
bne bloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Now stack has y/m/d/w/H/M/S/F
|
||||
|
||||
pla ; year
|
||||
sta DATELO+1
|
||||
|
||||
pla ; month
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta DATELO
|
||||
rol DATELO+1
|
||||
|
||||
pla ; day
|
||||
ora DATELO
|
||||
sta DATELO
|
||||
|
||||
pla ; skip week
|
||||
|
||||
pla ; hour
|
||||
sta TIMELO+1
|
||||
|
||||
pla ; minute
|
||||
sta TIMELO
|
||||
|
||||
pla ; skip seconds
|
||||
pla ; skip fraction
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Finish up
|
||||
|
||||
pla
|
||||
bmi done
|
||||
st4: sta $CFFF ; self-modified ($CFFF or SLOTCXROM)
|
||||
done: plp
|
||||
rts
|
||||
|
||||
unlock:
|
||||
;; NSC unlock sequence
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
|
||||
sizeof_driver := * - driver
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
581
clocks/ns.clock/set.datetime.s
Normal file
581
clocks/ns.clock/set.datetime.s
Normal file
|
@ -0,0 +1,581 @@
|
|||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.define PRODUCT "No-Slot Clock"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.org $2000
|
||||
|
||||
.proc main
|
||||
jsr detect_nsc
|
||||
bcc :+
|
||||
rts
|
||||
:
|
||||
;; --------------------------------------------------
|
||||
;; Prompt for Date
|
||||
|
||||
date:
|
||||
jsr zstrout
|
||||
scrcode "\rDate: WWW MM/DD/YY\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
lda INPUT_BUFFER+4
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+5
|
||||
jsr shift_into_tmp
|
||||
sta set_month
|
||||
jsr bcd_to_binary
|
||||
cmp #13
|
||||
bcs date
|
||||
|
||||
lda INPUT_BUFFER+7
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+8
|
||||
jsr shift_into_tmp
|
||||
sta set_date
|
||||
jsr bcd_to_binary
|
||||
cmp #32
|
||||
bcs date
|
||||
|
||||
lda INPUT_BUFFER+10
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+11
|
||||
jsr shift_into_tmp
|
||||
sta set_year
|
||||
|
||||
ldx #0
|
||||
ldy #1
|
||||
dow_loop:
|
||||
CHAR_MASK = $5F ; clear high bit and force uppercase
|
||||
|
||||
lda INPUT_BUFFER+0
|
||||
and #CHAR_MASK
|
||||
cmp wkdays,x
|
||||
bne next
|
||||
lda INPUT_BUFFER+1
|
||||
and #CHAR_MASK
|
||||
cmp wkdays+1,x
|
||||
bne next
|
||||
lda INPUT_BUFFER+2
|
||||
and #CHAR_MASK
|
||||
cmp wkdays+2,x
|
||||
bne next
|
||||
|
||||
sty set_dow
|
||||
jmp time
|
||||
|
||||
next: inx
|
||||
inx
|
||||
inx
|
||||
iny
|
||||
cpy #8
|
||||
bcc dow_loop
|
||||
jmp date
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Prompt for Time
|
||||
|
||||
time:
|
||||
jsr zstrout
|
||||
scrcode "\rTime: HH:MM:SS\x08\x08\x08\x08\x08\x08\x08\x08"
|
||||
.byte 0
|
||||
|
||||
jsr GETLN2
|
||||
|
||||
lda INPUT_BUFFER+0
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+1
|
||||
jsr shift_into_tmp
|
||||
sta set_hours
|
||||
jsr bcd_to_binary
|
||||
cmp #24
|
||||
bcs time
|
||||
|
||||
lda INPUT_BUFFER+3
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+4
|
||||
jsr shift_into_tmp
|
||||
sta set_minutes
|
||||
jsr bcd_to_binary
|
||||
cmp #60
|
||||
bcs time
|
||||
|
||||
lda INPUT_BUFFER+6
|
||||
jsr shift_into_tmp
|
||||
lda INPUT_BUFFER+7
|
||||
jsr shift_into_tmp
|
||||
sta set_seconds
|
||||
jsr bcd_to_binary
|
||||
cmp #60
|
||||
bcs time
|
||||
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Set NSC
|
||||
|
||||
jmp set_datetime
|
||||
|
||||
;;; --------------------------------------------------
|
||||
|
||||
bcd_to_binary:
|
||||
ldy #$FF ; result = -1
|
||||
sec
|
||||
sed
|
||||
: iny ; result += 1
|
||||
sbc #1 ; value -= 1
|
||||
bcs :-
|
||||
cld
|
||||
tya ; A = result
|
||||
rts
|
||||
|
||||
;;; --------------------------------------------------
|
||||
|
||||
shift_into_tmp:
|
||||
pha
|
||||
lda tmp
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta tmp
|
||||
pla
|
||||
and #%00001111
|
||||
ora tmp
|
||||
sta tmp
|
||||
rts
|
||||
|
||||
tmp: .byte 0
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
set_year: .byte 0
|
||||
set_month: .byte 0
|
||||
set_date: .byte 0
|
||||
set_dow: .byte 0
|
||||
set_hours: .byte 0
|
||||
set_minutes: .byte 0
|
||||
set_seconds: .byte 0
|
||||
set_hundredths: .byte 0
|
||||
|
||||
wkdays: .byte "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc detect_nsc
|
||||
;; Preserve date/time
|
||||
ldy #3 ; copy 4 bytes
|
||||
: lda DATELO,y
|
||||
sta saved,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Check slot ROMs
|
||||
lda #>$CFFF
|
||||
ldy #<$CFFF
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #0
|
||||
sta slot
|
||||
lda #3 ; treat slot 0 as slot 3
|
||||
|
||||
sloop: ora #$C0 ; A=$Cs
|
||||
pha
|
||||
jsr DetectZ80
|
||||
pla ; A=$Cs
|
||||
bcs next ; Z80 present, skip this slot
|
||||
|
||||
sta st1+2
|
||||
rloop: sta ld1+2
|
||||
sta ld2+2
|
||||
sta st2+2
|
||||
|
||||
lda #3 ; 3 tries - need valid results each time
|
||||
sta tries
|
||||
try: jsr driver ; try reading date/time
|
||||
lda DATELO+1 ; check result
|
||||
ror a
|
||||
lda DATELO
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #$0F
|
||||
beq next
|
||||
cmp #13 ; month
|
||||
bcs next
|
||||
lda DATELO
|
||||
and #$1F
|
||||
beq next
|
||||
cmp #32 ; day
|
||||
bcs next
|
||||
lda TIMELO+1
|
||||
cmp #24 ; hours
|
||||
bcs next
|
||||
lda TIMELO
|
||||
cmp #60 ; minutes
|
||||
bcs next
|
||||
dec tries
|
||||
bne try
|
||||
beq found
|
||||
next: inc slot
|
||||
lda slot
|
||||
cmp #8
|
||||
bcc sloop ; next slot
|
||||
bne not_found
|
||||
|
||||
;; Not found in slot ROM, try main ROMs ???
|
||||
lda #>$C015 ; $C015 = RDCXROM (internal or slot ROM?)
|
||||
ldy #<$C015
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
ldy #$07 ; $C007 = INTCXROM (read internal ROM)
|
||||
sta st1+2
|
||||
sty st1+1
|
||||
dey ; $C006 = SLOTCXROM (read slot ROM)
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #>$C800
|
||||
bne rloop
|
||||
|
||||
;; Restore date/time
|
||||
not_found:
|
||||
ldy #3
|
||||
: lda saved,y
|
||||
sta DATELO,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Show failure message
|
||||
jsr zstrout
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
|
||||
sec ; failure
|
||||
rts
|
||||
|
||||
found:
|
||||
clc ; success
|
||||
rts
|
||||
|
||||
saved: .byte 0, 0, 0, 0
|
||||
tries: .byte 3
|
||||
slot: .byte 0
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect Z80
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; This routine gets swapped into $0FFD for execution
|
||||
.proc Z80Routine
|
||||
target := $0FFD
|
||||
;; .org $FFFD
|
||||
patch := *+2
|
||||
.byte $32, $00, $e0 ; ld ($Es00),a ; s=slot being probed turn off Z80, next PC is $0000
|
||||
.byte $3e, $01 ; ld a,$01
|
||||
.byte $32, $08, $00 ; ld (flag),a
|
||||
.byte $c3, $fd, $ff ; jp $FFFD
|
||||
flag := *
|
||||
.byte $00 ; flag: .db $00
|
||||
.endproc
|
||||
.assert Z80Routine > Z80Routine::target + .sizeof(Z80Routine), error, "Z80 collision"
|
||||
|
||||
;;; Input: A = $Cn where n = slot number
|
||||
;;; Output: C=1 if Z80 found in slot
|
||||
.proc DetectZ80
|
||||
;; Location to poke to invoke Z80
|
||||
sta store+1
|
||||
|
||||
;; Convert $Cn to $En, update Z80 code
|
||||
ora #$E0
|
||||
sta Z80Routine::patch
|
||||
|
||||
;; Clear detection flag
|
||||
copy #0, Z80Routine::flag
|
||||
|
||||
;; Put routine in place
|
||||
jsr SwapRoutine
|
||||
|
||||
;; Try to invoke Z80
|
||||
php
|
||||
sei
|
||||
store := *+1
|
||||
sta $C000 ; self-modified
|
||||
plp
|
||||
|
||||
;; Restore memory
|
||||
jsr SwapRoutine
|
||||
|
||||
;; Flag will be set to 1 by routine if Z80 was present.
|
||||
lda Z80Routine::flag
|
||||
ror ; move flag into carry
|
||||
rts
|
||||
|
||||
.proc SwapRoutine
|
||||
ldx #.sizeof(Z80Routine)-1
|
||||
: ldy Z80Routine::target,x
|
||||
lda Z80Routine,x
|
||||
sta Z80Routine::target,x
|
||||
tya
|
||||
sta Z80Routine,x
|
||||
dex
|
||||
bpl :-
|
||||
rts
|
||||
.endproc
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; NSC driver - modified as needed and copied into ProDOS
|
||||
;;; ============================================================
|
||||
|
||||
driver:
|
||||
php
|
||||
sei
|
||||
lda $C00B ; Ultrawarp bug workaround c/o @bobbimanners
|
||||
ld4: lda $CFFF ; self-modified ($CFFF or RDCXROM)
|
||||
pha
|
||||
st1: sta $C300 ; self-modified ($Cn00 or INTCXROM)
|
||||
ld1: lda $C304 ; self-modified ($Cn04)
|
||||
ldx #8
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Unlock the NSC by bit-banging.
|
||||
uloop:
|
||||
unlock_addr := *+1
|
||||
lda unlock-1,x ; self-modified (during relocation)
|
||||
sec
|
||||
ror a ; a bit at a time
|
||||
: pha
|
||||
lda #0
|
||||
rol a
|
||||
tay
|
||||
ld2: lda $C300,y ; self-modified ($Cn00)
|
||||
pla
|
||||
lsr a
|
||||
bne :-
|
||||
dex
|
||||
bne uloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Read 8 bytes * 8 bits of clock data, push onto stack
|
||||
|
||||
tmp := $200
|
||||
ldx #8
|
||||
bloop: ldy #8
|
||||
st2:
|
||||
: lda $C304 ; self-modified ($Cn04)
|
||||
ror a
|
||||
ror tmp
|
||||
dey
|
||||
bne :-
|
||||
|
||||
;; BCD to Binary - slow but tiny
|
||||
lda tmp ; A = value
|
||||
ldy #$FF ; result = -1
|
||||
sec
|
||||
sed
|
||||
: iny ; result += 1
|
||||
sbc #1 ; value -= 1
|
||||
bcs :-
|
||||
cld
|
||||
tya ; A = result
|
||||
|
||||
;; Push to stack
|
||||
pha
|
||||
dex
|
||||
bne bloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Now stack has y/m/d/w/H/M/S/F
|
||||
|
||||
pla ; year
|
||||
sta DATELO+1
|
||||
|
||||
pla ; month
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta DATELO
|
||||
rol DATELO+1
|
||||
|
||||
pla ; day
|
||||
ora DATELO
|
||||
sta DATELO
|
||||
|
||||
pla ; skip week
|
||||
|
||||
pla ; hour
|
||||
sta TIMELO+1
|
||||
|
||||
pla ; minute
|
||||
sta TIMELO
|
||||
|
||||
pla ; skip seconds
|
||||
pla ; skip fraction
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Finish up
|
||||
|
||||
pla
|
||||
bmi done
|
||||
st4: sta $CFFF ; self-modified ($CFFF or SLOTCXROM)
|
||||
done: plp
|
||||
rts
|
||||
|
||||
unlock:
|
||||
;; NSC unlock sequence
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
|
||||
sizeof_driver := * - driver
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc set_datetime
|
||||
jsr patch_from_driver
|
||||
|
||||
php
|
||||
sei
|
||||
lda $C00B ; Ultrawarp bug workaround c/o @bobbimanners
|
||||
ld4: lda $CFFF ; self-modified ($CFFF or RDCXROM)
|
||||
pha
|
||||
st1: sta $C300 ; self-modified ($Cn00 or INTCXROM)
|
||||
ld1: lda $C304 ; self-modified ($Cn04)
|
||||
ldx #8
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Unlock the NSC by bit-banging.
|
||||
uloop:
|
||||
unlock_addr := *+1
|
||||
lda unlock-1,x ; self-modified (during relocation)
|
||||
sec
|
||||
ror a ; a bit at a time
|
||||
: pha
|
||||
lda #0
|
||||
rol a
|
||||
tay
|
||||
ld2: lda $C300,y ; self-modified ($Cn00)
|
||||
pla
|
||||
lsr a
|
||||
bne :-
|
||||
dex
|
||||
bne uloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Push data onto stack
|
||||
|
||||
lda set_year
|
||||
pha
|
||||
lda set_month
|
||||
pha
|
||||
lda set_date
|
||||
pha
|
||||
lda set_dow
|
||||
pha
|
||||
lda set_hours
|
||||
pha
|
||||
lda set_minutes
|
||||
pha
|
||||
lda set_seconds
|
||||
pha
|
||||
lda #$00 ; Hundredths
|
||||
pha
|
||||
|
||||
;; --------------------------------------------------
|
||||
|
||||
;; Loop over and write all 64 bits into register
|
||||
tmp := $200
|
||||
lda #8 ; bytes
|
||||
sta count
|
||||
|
||||
bloop: ldx #8 ; bits
|
||||
: pla ; current byte
|
||||
ror a ; shift out bit to write
|
||||
pha ; not done with this
|
||||
lda #0
|
||||
rol a ; shift into low bit
|
||||
tay ; and into Y to use as index
|
||||
st2:
|
||||
lda $C300,y ; self-modified ($Cn00)
|
||||
|
||||
dex ; next bit
|
||||
bne :-
|
||||
|
||||
pla ; next byte
|
||||
dec count
|
||||
bne bloop
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Finish up
|
||||
|
||||
pla
|
||||
bmi done
|
||||
st4: sta $CFFF ; self-modified ($CFFF or SLOTCXROM)
|
||||
done: plp
|
||||
rts
|
||||
|
||||
count: .byte 0
|
||||
.endproc
|
||||
|
||||
;;; --------------------------------------------------
|
||||
|
||||
.proc patch_from_driver
|
||||
copy ld1+2, set_datetime::ld1+2
|
||||
copy ld2+2, set_datetime::ld2+2
|
||||
copy16 ld4+1, set_datetime::ld4+1
|
||||
|
||||
copy16 st1+1, set_datetime::st1+1
|
||||
copy st2+2, set_datetime::st2+2
|
||||
copy16 st4+1, set_datetime::st4+1
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
46
clocks/romx/Makefile
Normal file
46
clocks/romx/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/romxrtc.system.SYS \
|
||||
$(OUTDIR)/romxrtc.setup.SYS
|
||||
|
||||
LOG_SUCCESS=1
|
||||
LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
5
clocks/romx/README.md
Normal file
5
clocks/romx/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# ROMX ProDOS Clock Driver
|
||||
|
||||
Original source c/o Jeff Mazur.
|
||||
|
||||
`ROMXRTC.SETUP` is for use with [SETUP.SYSTEM](../../setup/README.md).
|
332
clocks/romx/romxrtc.system.s
Normal file
332
clocks/romx/romxrtc.system.s
Normal file
|
@ -0,0 +1,332 @@
|
|||
;;; ROMX ProDOS RTC Driver
|
||||
;;; Ver 0.91
|
||||
;;; Ver 0.92 Added ZIP slowdowns
|
||||
;;; Ver 0.93 Moved StubLoc to $0110
|
||||
;;; Ver 0.94 Moved StubLoc to RTC_BUF + 7. Save/restore in stack
|
||||
;;; Ver 0.95 Moved MaskTable to after Thunderclock reloc area
|
||||
;;;
|
||||
;;; Modifications by Joshua Bell inexorabletash@gmail.com
|
||||
;;; * Converted to ca65 syntax and adapted to driver wrapper.
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../../inc/apple2.inc"
|
||||
.include "../../inc/macros.inc"
|
||||
.include "../../inc/prodos.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
;;; Uncomment the following to "fake" a clock with a fixed date.
|
||||
;;; Used for testing without a real ROMX around.
|
||||
;;; FAKE_CLOCK = 1
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_preamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
||||
|
||||
ZipSlo := $C0E0 ; ZIP CHIP slowdown
|
||||
|
||||
;;; ROMX locations
|
||||
FWReadClock := $D8F0 ; Firmware clock driver routine
|
||||
SigCk := $DFFE ; ROMX sig bytes
|
||||
SEL_MBANK := $F851 ; Select Main bank reg
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "ROMX Clock"
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ensure there is not a previous clock driver installed.
|
||||
|
||||
.ifdef FAKE_CLOCK
|
||||
|
||||
maybe_install_driver := install_driver
|
||||
|
||||
.else
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_romx ; nope, check for ROMX
|
||||
|
||||
rts ; yes, done!
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc detect_romx
|
||||
;; Try to detect ROMX and RTC
|
||||
bit ROMIN2 ; enable ROM
|
||||
bit ZipSlo ; disable ZIP
|
||||
bit $FACA ; enable ROMXe, temp bank 0
|
||||
bit $FACA
|
||||
bit $FAFE
|
||||
|
||||
lda SigCk ; Check for ROMX signature bytes
|
||||
cmp #$4A
|
||||
bne nope
|
||||
lda SigCk+1
|
||||
cmp #$CD
|
||||
bne nope
|
||||
lda FWReadClock ; is RTC code there?
|
||||
cmp #$AD
|
||||
bne nope
|
||||
clc ; found clock!
|
||||
bcc :+
|
||||
nope: sec ; not found
|
||||
: bit SEL_MBANK ; restore original bank (unconditionally)
|
||||
bcc install_driver ; found clock!
|
||||
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.if ::LOG_FAILURE
|
||||
;; Show failure message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found."
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
|
||||
sec ; failure
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.endif
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install ROMX RTC Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
;; Update absolute addresses within driver
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
|
||||
lda ptr
|
||||
clc
|
||||
adc RELOC1
|
||||
sta RELOC1
|
||||
lda ptr + 1
|
||||
adc RELOC1 + 1
|
||||
sta RELOC1 + 1
|
||||
|
||||
lda ptr
|
||||
clc
|
||||
adc RELOC2
|
||||
sta RELOC2
|
||||
lda ptr + 1
|
||||
adc RELOC2 + 1
|
||||
sta RELOC2 + 1
|
||||
|
||||
;; Copy driver into appropriate bank
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #ClockDrvSize-1
|
||||
|
||||
loop: lda ClockDrv,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
lda ROMIN2
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
;; Display success message
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
|
||||
;; Display the current date
|
||||
jsr cout_date
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
clc ; success
|
||||
rts ; done!
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; ROMX RTC driver - Relocated into ProDOS clock driver space
|
||||
;;; ============================================================
|
||||
|
||||
;;; ROMX Firmware writes RTC data into this fixed location.
|
||||
;;; It risks conflicting with some applications (e.g. A2DeskTop),
|
||||
;;; so the data is saved/restored around clock reads.
|
||||
|
||||
RTC_BUF := $02B0 ; use keyboard buffer
|
||||
StubLoc := RTC_BUF+7 ; RAM stub for ROMX
|
||||
|
||||
|
||||
ClockDrv:
|
||||
;; --------------------------------------------------
|
||||
;; Enter driver
|
||||
|
||||
php
|
||||
sei
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Copy the stub to RAM, and preserve RTC_BUF
|
||||
|
||||
ldx #RamStubEnd-RamStub+7 ; preserve and copy stub to RAM
|
||||
: lda RTC_BUF,x
|
||||
pha
|
||||
RELOC1 := *+1
|
||||
lda RamStub-ClockDrv-7,x ; self-modified during relocation
|
||||
sta RTC_BUF,x
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Read the clock into `RTC_BUF`
|
||||
|
||||
jsr StubLoc
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Strip non-number bits, convert decimal to binary, push to stack
|
||||
|
||||
ldy #6
|
||||
bufloop:
|
||||
lda RTC_BUF,y
|
||||
|
||||
RELOC2 := *+1
|
||||
and MaskTable-1-ClockDrv,y ; self-modified during relocation
|
||||
|
||||
;; BCD to Binary
|
||||
;; On entry, A=BCD value &00-&99
|
||||
;; On exit, A=binary value 0-99
|
||||
ldx #$FF ; Start with result=-1
|
||||
sec ; Prepare for subtraction
|
||||
sed ; Switch to Decimal arithmetic
|
||||
: inx ; Add 1 to result
|
||||
sbc #1 ; Subtract 1 with BCD arithmetic
|
||||
bcs :- ; Loop until BCD value < 0
|
||||
cld ; Switch back to Binary arithmetic
|
||||
txa ; return in A
|
||||
|
||||
;; Push to stack
|
||||
pha
|
||||
dey
|
||||
bne bufloop ; 6..1
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Pull and place values into ProDOS time locations
|
||||
|
||||
;; (`RTC_BUF`+0 is not pushed)
|
||||
|
||||
pla ; `RTC_BUF`+1 = minute
|
||||
sta TIMELO
|
||||
|
||||
pla ; `RTC_BUF`+2 = hour
|
||||
sta TIMEHI
|
||||
|
||||
pla ; `RTC_BUF`+3 = weekday (unused)
|
||||
|
||||
pla ; `RTC_BUF`+4 = day
|
||||
sta DATELO
|
||||
|
||||
pla ; `RTC_BUF`+5 = month
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a ; MSB will merge into DATEHI
|
||||
ora DATELO ; merge with day
|
||||
sta DATELO
|
||||
|
||||
pla ; `RTC_BUF`+6 = year
|
||||
sta DATEHI
|
||||
rol DATEHI ; merge with MSB from month
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Restore what was originally at `RTC_BUF`
|
||||
|
||||
ldx #0
|
||||
: pla
|
||||
sta RTC_BUF,x
|
||||
inx
|
||||
cpx #RamStubEnd-RamStub+7+1
|
||||
bne :-
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Exit driver
|
||||
|
||||
plp
|
||||
rts
|
||||
|
||||
RamStub:
|
||||
.ifndef FAKE_CLOCK
|
||||
;; Really read the ROMX RTC
|
||||
|
||||
bit ROMIN2 ; enable ROM
|
||||
bit ZipSlo ; disable ZIP
|
||||
bit $FACA ; enable ROMXe, temp bank 0
|
||||
bit $FACA
|
||||
bit $FAFE
|
||||
jsr FWReadClock ; Call ROMX to read clock
|
||||
bit SEL_MBANK ; restore original bank
|
||||
bit LCBANK1 ; restore LC w/write
|
||||
bit LCBANK1
|
||||
.else
|
||||
;; No ROMX RTC around? Provide fake data for testing.
|
||||
;; October 5, 2021 12:34:56
|
||||
|
||||
lda #$56 ; sec
|
||||
sta RTC_BUF+0
|
||||
lda #$34 ; min
|
||||
sta RTC_BUF+1
|
||||
lda #$12 ; hr
|
||||
sta RTC_BUF+2
|
||||
lda #$05 ; date
|
||||
sta RTC_BUF+4
|
||||
lda #$10 ; month
|
||||
sta RTC_BUF+5
|
||||
lda #$21 ; year
|
||||
sta RTC_BUF+6
|
||||
.endif
|
||||
|
||||
rts
|
||||
RamStubEnd := *
|
||||
.assert RamStubEnd - RamStub < $20, error, "Stub too long"
|
||||
|
||||
MaskTable:
|
||||
.byte $7f, $3f, $07, $3f, $1f, $ff
|
||||
;; .... min hour wkdy date mnth year (`RTC_BUF` bytes 1..6)
|
||||
|
||||
ClockDrvEnd := *
|
||||
ClockDrvSize = ClockDrvEnd - ClockDrv
|
||||
|
||||
.assert ClockDrvSize <= 125, error, \
|
||||
.sprintf("Clock driver must be <= 125 bytes, was %d bytes", ClockDrvSize)
|
||||
|
||||
;;; ************************************************************
|
||||
.ifndef JUMBO_CLOCK_DRIVER
|
||||
.include "../../inc/driver_postamble.inc"
|
||||
.endif ; JUMBO_CLOCK_DRIVER
|
||||
;;; ************************************************************
|
120
common.inc
120
common.inc
|
@ -1,120 +0,0 @@
|
|||
;;; ------------------------------------------------------------
|
||||
;;; ASCII
|
||||
|
||||
BELL := $07
|
||||
CR := $0D
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Constants
|
||||
|
||||
MAX_DW := $FFFF
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Softswitches
|
||||
|
||||
CLR80VID := $C00C ; 40 Columns
|
||||
ROMIN2 := $C082 ; Read ROM; no write
|
||||
RWRAM1 := $C08B ; Read/write RAM bank 1
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; ProDOS
|
||||
|
||||
PRODOS := $BF00
|
||||
DATETIME := $BF06
|
||||
DEVNUM := $BF30
|
||||
BITMAP := $BF58
|
||||
BITMAP_SIZE := 24 ; 24 bytes in system bit map
|
||||
DATELO := $BF90
|
||||
TIMELO := $BF92
|
||||
MACHID := $BF98
|
||||
|
||||
SYS_ADDR := $2000 ; Load address for SYSTEM files
|
||||
PATHNAME := $0280 ; Pathname of loaded system file
|
||||
|
||||
;;; MLI commands
|
||||
MLI_QUIT := $65
|
||||
MLI_READ_BLOCK := $80
|
||||
MLI_GET_TIME := $82
|
||||
MLI_OPEN := $C8
|
||||
MLI_READ := $CA
|
||||
MLI_CLOSE := $CC
|
||||
|
||||
.macro PRODOS_CALL call, params
|
||||
jsr PRODOS
|
||||
.byte call
|
||||
.addr params
|
||||
.endmacro
|
||||
|
||||
;;; Volume Directory Block Header structure
|
||||
.scope VolumeDirectoryBlockHeader
|
||||
prev_block := $00
|
||||
next_block := $02
|
||||
entry_length := $23
|
||||
entries_per_block := $24
|
||||
header_length := $2B
|
||||
.endscope
|
||||
|
||||
;; File Entry structure
|
||||
.scope FileEntry
|
||||
storage_type := $00
|
||||
name_length := $00
|
||||
file_name := $01
|
||||
file_type := $10
|
||||
.endscope
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Monitor
|
||||
|
||||
INIT := $FB2F
|
||||
MON_HOME := $FC58
|
||||
CROUT := $FD8E
|
||||
PRBYTE := $FDDA
|
||||
COUT := $FDED
|
||||
SETNORM := $FE84
|
||||
SETKBD := $FE89
|
||||
SETVID := $FE93
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; I/O Registers (for Slot 2)
|
||||
|
||||
TDREG := $C088 + $20 ; ACIA Transmit Register (write)
|
||||
RDREG := $C088 + $20 ; ACIA Receive Register (read)
|
||||
STATUS := $C089 + $20 ; ACIA Status/Reset Register
|
||||
COMMAND := $C08A + $20 ; ACIA Command Register (read/write)
|
||||
CONTROL := $C08B + $20 ; ACIA Control Register (read/write)
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; Length-prefixed string
|
||||
.macro PASCAL_STRING arg
|
||||
.byte .strlen(arg)
|
||||
.byte arg
|
||||
.endmacro
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; Define a string with high bits set
|
||||
;;; e.g. HIASCII "Ding ding", $7, $7
|
||||
.macro HIASCII arg, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
|
||||
.if .blank(arg)
|
||||
.exitmacro
|
||||
.endif
|
||||
.if .match ({arg}, "") ; string?
|
||||
.repeat .strlen(arg), i
|
||||
.byte .strat(arg, i) | $80
|
||||
.endrep
|
||||
.else ; otherwise assume number/char/identifier
|
||||
.byte (arg | $80)
|
||||
.endif
|
||||
HIASCII arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
|
||||
.endmacro
|
||||
|
||||
;;; Like HIASCII, but null-terminated
|
||||
.macro HIASCIIZ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
|
||||
HIASCII arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
|
||||
.byte 0
|
||||
.endmacro
|
||||
|
||||
;;; Set the high bit on the passed byte
|
||||
.define HI(c) ((c)|$80)
|
737
cricket.system.s
737
cricket.system.s
|
@ -1,737 +0,0 @@
|
|||
;;; The Cricket Clock - ProDOS System
|
||||
;;; Adapted from /CRICKET/PRODOS.MOD
|
||||
;;; Original: Street Electronics Corporation (C) 1984
|
||||
|
||||
;;; Adapted from: /NO.SLOT.CLOCK/NS.CLOCK.SYSTEM
|
||||
;;; Original by "CAP" 04/21/91
|
||||
;;; http://www.apple2.org.za/gswv/a2zine/GS.WorldView/v1999/Oct/MISC/NSC.Disk.TXT
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "./common.inc"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
data_buffer = $1800
|
||||
|
||||
read_delay_hi = $3 * 3 ; ($300 iterations is normal * 3.6MHz)
|
||||
|
||||
.define SYSTEM_SUFFIX ".SYSTEM"
|
||||
.define PRODUCT "Cricket Clock"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.org $1000
|
||||
|
||||
;; Loaded at $2000 but relocates to $1000
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
sys_start:
|
||||
sec
|
||||
bcs relocate
|
||||
|
||||
.byte MM, DD, YY ; version date stamp
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Relocate this code from $2000 (.SYSTEM start location) to $1000
|
||||
;;; and start executing there. This is done so that the next .SYSTEM
|
||||
;;; file can be loaded/run at $2000.
|
||||
|
||||
.proc relocate
|
||||
src := SYS_ADDR
|
||||
dst := $1000
|
||||
|
||||
ldx #(sys_end - sys_start + $FF) / $100 ; pages
|
||||
ldy #0
|
||||
load: lda src,y ; self-modified
|
||||
load_hi := *-1
|
||||
sta dst,y ; self-modified
|
||||
store_hi := *-1
|
||||
iny
|
||||
bne load
|
||||
inc load_hi
|
||||
inc store_hi
|
||||
dex
|
||||
beq find_self_name ; done
|
||||
jmp load
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Identify the name of this SYS file, which should be present at
|
||||
;;; $280 with or without a path prefix. This is used when searching
|
||||
;;; for the next .SYSTEM file to execute.
|
||||
|
||||
.proc find_self_name
|
||||
;; Search pathname buffer backwards for '/', then
|
||||
;; copy name into |self_name|; this is used later
|
||||
;; to find/invoke the next .SYSTEM file.
|
||||
|
||||
;; Find '/' (which may not be present, prefix is optional)
|
||||
lda #0
|
||||
sta $A8
|
||||
ldx PATHNAME
|
||||
beq pre_install
|
||||
floop: inc $A8
|
||||
dex
|
||||
beq copy
|
||||
lda PATHNAME,x
|
||||
eor #'/'
|
||||
asl a
|
||||
bne floop
|
||||
|
||||
;; Copy name into |self_name| buffer
|
||||
copy: ldy #0
|
||||
cloop: iny
|
||||
inx
|
||||
lda PATHNAME,x
|
||||
sta self_name,y
|
||||
cpy $A8
|
||||
bcc cloop
|
||||
sty self_name
|
||||
.endproc
|
||||
;; Fall through...
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Before installing, get the system to a known state and
|
||||
;;; ensure there is not a previous clock driver installed.
|
||||
|
||||
.proc pre_install
|
||||
cld
|
||||
bit ROMIN2
|
||||
|
||||
;; Update reset vector - re-invokes this code.
|
||||
lda #<pre_install
|
||||
sta $03F2
|
||||
lda #>pre_install
|
||||
sta $03F3
|
||||
eor #$A5
|
||||
sta $03F4
|
||||
|
||||
;; Quit 80-column firmware
|
||||
lda #$95 ; Ctrl+U (quit 80 col firmware)
|
||||
jsr COUT
|
||||
|
||||
;; Reset stack
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
;; Reset I/O
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
jsr SETNORM
|
||||
jsr INIT
|
||||
|
||||
;; Update System Bit Map
|
||||
ldx #BITMAP_SIZE-1
|
||||
lda #%00000001 ; protect page $BF
|
||||
: sta BITMAP,x
|
||||
lda #%00000000 ; nothing else protected until...
|
||||
dex
|
||||
bne :-
|
||||
lda #%11001111 ; ZP ($00), stack ($01), text page 1 ($04-$07)
|
||||
sta BITMAP
|
||||
|
||||
lda MACHID
|
||||
and #$88 ; IIe or IIc (or IIgs) ?
|
||||
bne :+
|
||||
lda #$DF
|
||||
sta lowercase_mask ; lower case to upper case
|
||||
|
||||
: lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_cricket ; nope, check for Cricket
|
||||
|
||||
;; Chain with no message
|
||||
jmp launch_next_sys_file
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect Cricket. Detect SSC and if present probe device.
|
||||
|
||||
.proc detect_cricket
|
||||
|
||||
;; Check Slot 2 for SSC. ID bytes per:
|
||||
;; Apple II Technical Note #8: Pascal 1.1 Firmware Protocol ID Bytes
|
||||
lda $C205
|
||||
cmp #$38
|
||||
bne ssc_not_found
|
||||
lda $C207
|
||||
cmp #$18
|
||||
bne ssc_not_found
|
||||
lda $C20B
|
||||
cmp #$01
|
||||
bne ssc_not_found
|
||||
lda $C20C
|
||||
cmp #$31
|
||||
bne ssc_not_found
|
||||
|
||||
beq init_ssc
|
||||
ssc_not_found:
|
||||
jmp not_found
|
||||
|
||||
;; Init SSC and try the "Read Cricket ID code" sequence.
|
||||
init_ssc:
|
||||
lda COMMAND ; save status of SSC registers
|
||||
sta saved_command
|
||||
lda CONTROL
|
||||
sta saved_control
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Read Cricket ID code: 00 ($00)
|
||||
lda #0
|
||||
jsr sendbyte
|
||||
|
||||
;; "The Cricket will return a "C" (195, $C3) followed by a
|
||||
;; version number (in ASCII) and a carriage return (141, $8D)."
|
||||
jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
cmp #HI('C') ; = 'C' ?
|
||||
bne cricket_not_found
|
||||
|
||||
jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
bcc digit
|
||||
|
||||
: jsr readbyte
|
||||
bcs cricket_not_found ; timeout
|
||||
cmp #HI(CR) ; = CR ?
|
||||
beq cricket_found
|
||||
digit: cmp #HI('0') ; < '0' ?
|
||||
bcc cricket_not_found
|
||||
cmp #HI('9' + 1) ; > '9' ?
|
||||
bcs cricket_not_found
|
||||
bcc :-
|
||||
|
||||
cricket_found:
|
||||
jmp install_driver
|
||||
|
||||
cricket_not_found:
|
||||
lda saved_control
|
||||
sta CONTROL
|
||||
lda saved_command
|
||||
sta COMMAND
|
||||
;; fall through...
|
||||
|
||||
not_found:
|
||||
;; Show failure message
|
||||
jsr MON_HOME
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, PRODUCT, " - Not Found."
|
||||
jmp launch_next_sys_file
|
||||
|
||||
saved_command: .byte 0
|
||||
saved_control: .byte 0
|
||||
.endproc
|
||||
|
||||
;; Write byte in A
|
||||
.proc sendbyte
|
||||
pha
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
pla
|
||||
sta TDREG
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;; Read byte into A, or carry set if timed out
|
||||
.proc readbyte
|
||||
tries := $100 * read_delay_hi
|
||||
counter := $A5
|
||||
|
||||
lda #<tries
|
||||
sta counter
|
||||
lda #>tries
|
||||
sta counter+1
|
||||
|
||||
check: lda STATUS ; did we get it?
|
||||
and #(1 << 3) ; receive register full? (bit 3)
|
||||
bne ready ; yes, we read the value
|
||||
|
||||
dec counter
|
||||
bne check
|
||||
dec counter+1
|
||||
bne check
|
||||
|
||||
sec ; failed
|
||||
rts
|
||||
|
||||
ready: lda RDREG ; actually read the register
|
||||
clc
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install Cricket Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #sizeof_driver-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
;; Display success message
|
||||
bit ROMIN2
|
||||
jsr MON_HOME
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, PRODUCT, " - Installed "
|
||||
|
||||
;; Display the current date
|
||||
lda DATELO+1 ; month
|
||||
ror a
|
||||
pha
|
||||
lda DATELO
|
||||
pha
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #%00001111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; day
|
||||
and #%00011111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; year
|
||||
jsr cout_number
|
||||
jsr CROUT
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Find and invoke the next .SYSTEM file
|
||||
|
||||
.proc launch_next_sys_file
|
||||
;; Update reset vector - now terminates.
|
||||
lda #<quit
|
||||
sta $03F2
|
||||
lda #>quit
|
||||
sta $03F3
|
||||
eor #$A5
|
||||
sta $03F4
|
||||
|
||||
ptr := $A5
|
||||
num := $A7
|
||||
len := $A8
|
||||
|
||||
lda DEVNUM ; stick with most recent device
|
||||
sta read_block_params_unit_num
|
||||
jsr read_block
|
||||
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::entry_length
|
||||
sta entry_length_mod
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::entries_per_block
|
||||
sta entries_per_block_mod
|
||||
lda #1
|
||||
sta num
|
||||
|
||||
lda #<(data_buffer + VolumeDirectoryBlockHeader::header_length)
|
||||
sta ptr
|
||||
lda #>(data_buffer + VolumeDirectoryBlockHeader::header_length)
|
||||
sta ptr+1
|
||||
|
||||
;; Process directory entry
|
||||
entry: ldy #FileEntry::file_type ; file_type
|
||||
lda (ptr),y
|
||||
cmp #$FF ; type=SYS
|
||||
bne next
|
||||
ldy #FileEntry::storage_type
|
||||
lda (ptr),y
|
||||
and #$30 ; regular file (not directory, pascal)
|
||||
beq next
|
||||
lda (ptr),y
|
||||
and #$0F ; name_length
|
||||
sta len
|
||||
tay
|
||||
|
||||
;; Compare suffix - is it .SYSTEM?
|
||||
ldx #.strlen(SYSTEM_SUFFIX)-1
|
||||
: lda (ptr),y
|
||||
cmp suffix,x
|
||||
bne next
|
||||
dey
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
;; Yes; is it *this* .SYSTEM file?
|
||||
ldy self_name
|
||||
cpy len
|
||||
bne handle_sys_file
|
||||
: lda (ptr),y
|
||||
cmp self_name,y
|
||||
bne handle_sys_file
|
||||
dey
|
||||
bne :-
|
||||
sec
|
||||
ror found_self_flag
|
||||
|
||||
;; Move to the next entry
|
||||
next: lda ptr
|
||||
clc
|
||||
adc #$27 ; self-modified: entry_length
|
||||
entry_length_mod := *-1
|
||||
sta ptr
|
||||
bcc :+
|
||||
inc ptr+1
|
||||
: inc num
|
||||
lda num
|
||||
cmp #$0D ; self-modified: entries_per_block
|
||||
entries_per_block_mod := *-1
|
||||
bcc entry
|
||||
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::next_block
|
||||
sta read_block_params_block_num
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::next_block + 1
|
||||
sta read_block_params_block_num+1
|
||||
ora read_block_params_block_num
|
||||
beq not_found ; last block has next=0
|
||||
jsr read_block
|
||||
lda #0
|
||||
sta num
|
||||
lda #<(data_buffer + $04)
|
||||
sta ptr
|
||||
lda #>(data_buffer + $04)
|
||||
sta ptr+1
|
||||
jmp entry
|
||||
|
||||
;; Found a .SYSTEM file which is not this one; invoke
|
||||
;; it if follows this one.
|
||||
handle_sys_file:
|
||||
bit found_self_flag
|
||||
bpl next
|
||||
|
||||
;; Compose the path to invoke. First walk self path
|
||||
;; backwards to '/'.
|
||||
ldx PATHNAME
|
||||
beq append
|
||||
: dex
|
||||
beq append
|
||||
lda PATHNAME,x
|
||||
eor #'/'
|
||||
asl a
|
||||
bne :-
|
||||
|
||||
;; Now append name of found file.
|
||||
append: ldy #0
|
||||
: iny
|
||||
inx
|
||||
lda (ptr),y
|
||||
sta PATHNAME,x
|
||||
cpy len
|
||||
bcc :-
|
||||
stx PATHNAME
|
||||
jmp invoke_system_file
|
||||
|
||||
not_found:
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, "* Unable to find next '.SYSTEM' file *", CR
|
||||
bit KBDSTRB
|
||||
: lda KBD
|
||||
bpl :-
|
||||
bit KBDSTRB
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: cmp #(HI 'a') ; lower-case?
|
||||
bcc :+
|
||||
and lowercase_mask ; make upper-case if needed
|
||||
: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; COUT a 2-digit number in A
|
||||
|
||||
.proc cout_number
|
||||
ldx #(HI '0')
|
||||
cmp #10 ; >= 10?
|
||||
bcc tens
|
||||
|
||||
;; divide by 10, dividend(+'0') in x remainder in a
|
||||
: sbc #10
|
||||
inx
|
||||
cmp #10
|
||||
bcs :-
|
||||
|
||||
tens: pha
|
||||
cpx #(HI '0')
|
||||
beq units
|
||||
txa
|
||||
jsr COUT
|
||||
|
||||
units: pla
|
||||
ora #(HI '0')
|
||||
jsr COUT
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
lowercase_mask:
|
||||
.byte $FF ; Set to $DF on systems w/o lower-case
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Invoke ProDOS QUIT routine.
|
||||
|
||||
.proc quit
|
||||
PRODOS_CALL MLI_QUIT, quit_params
|
||||
.byte 0 ; crash if QUIT fails
|
||||
rts
|
||||
.proc quit_params
|
||||
.byte 4 ; param_count
|
||||
.byte 0 ; quit_type
|
||||
.word 0000 ; reserved
|
||||
.byte 0 ; reserved
|
||||
.word 0000 ; reserved
|
||||
.endproc
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Read a disk block.
|
||||
|
||||
.proc read_block
|
||||
PRODOS_CALL MLI_READ_BLOCK, read_block_params
|
||||
bcs on_error
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.proc read_block_params
|
||||
.byte 3 ; param_count
|
||||
unit_num: .byte $60 ; unit_num
|
||||
.addr data_buffer ; data_buffer
|
||||
block_num: .word 2 ; block_num - block 2 is volume directory
|
||||
.endproc
|
||||
read_block_params_unit_num := read_block_params::unit_num
|
||||
read_block_params_block_num := read_block_params::block_num
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Load/execute the system file in PATHNAME
|
||||
|
||||
.proc invoke_system_file
|
||||
PRODOS_CALL MLI_OPEN, open_params
|
||||
bcs on_error
|
||||
|
||||
lda open_params_ref_num
|
||||
sta read_params_ref_num
|
||||
|
||||
PRODOS_CALL MLI_READ, read_params
|
||||
bcs on_error
|
||||
|
||||
PRODOS_CALL MLI_CLOSE, close_params
|
||||
bcs on_error
|
||||
|
||||
jmp SYS_ADDR ; Invoke loaded SYSTEM file
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Error handler - invoked if any ProDOS error occurs.
|
||||
|
||||
.proc on_error
|
||||
pha
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, "** Disk Error $"
|
||||
pla
|
||||
jsr PRBYTE
|
||||
jsr zstrout
|
||||
HIASCIIZ " **", CR
|
||||
bit KBDSTRB
|
||||
: lda KBD
|
||||
bpl :-
|
||||
bit KBDSTRB
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc open_params
|
||||
.byte 3 ; param_count
|
||||
.addr PATHNAME ; pathname
|
||||
.addr data_buffer ; io_buffer
|
||||
ref_num:.byte 1 ; ref_num
|
||||
.endproc
|
||||
open_params_ref_num := open_params::ref_num
|
||||
|
||||
.proc read_params
|
||||
.byte 4 ; param_count
|
||||
ref_num:.byte 1 ; ref_num
|
||||
.addr SYS_ADDR ; data_buffer
|
||||
.word MAX_DW ; request_count
|
||||
.word 0 ; trans_count
|
||||
.endproc
|
||||
read_params_ref_num := read_params::ref_num
|
||||
|
||||
.proc close_params
|
||||
.byte 1 ; param_count
|
||||
ref_num:.byte 0 ; ref_num
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
found_self_flag:
|
||||
.byte 0
|
||||
|
||||
suffix: .byte SYSTEM_SUFFIX
|
||||
|
||||
self_name:
|
||||
PASCAL_STRING "CRICKET.SYSTEM"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Cricket Clock Driver - copied into ProDOS
|
||||
|
||||
.proc driver
|
||||
scratch := $3A ; ZP scratch location
|
||||
|
||||
;; Initialize
|
||||
php
|
||||
sei
|
||||
lda COMMAND ; save status of command register
|
||||
pha
|
||||
|
||||
;; Configure SSC
|
||||
lda #%00001011 ; no parity/echo/interrupts, RTS low, DTR low
|
||||
sta COMMAND
|
||||
lda #%10011110 ; 9600 baud, 8 data bits, 2 stop bits
|
||||
sta CONTROL
|
||||
|
||||
;; Send command
|
||||
: lda STATUS
|
||||
and #(1 << 4) ; transmit register empty? (bit 4)
|
||||
beq :- ; nope, keep waiting
|
||||
lda #HI('@') ; '@' command
|
||||
sta TDREG
|
||||
|
||||
read_len := 7 ; read 7 bytes (w/m/d/y/H/M/S)
|
||||
|
||||
;; Read response, pushing to stack
|
||||
ldy #(read_len-1)
|
||||
|
||||
rloop: ldx #0 ; x = retry loop counter low byte
|
||||
lda #read_delay_hi ; scratch = retry loop counter high byte
|
||||
sta scratch
|
||||
|
||||
check: lda STATUS ; did we get it?
|
||||
and #(1 << 3) ; receive register full? (bit 3)
|
||||
bne ready ; yes, we read the value
|
||||
|
||||
inx ; not yet, so keep trying
|
||||
bne check ; until counter runs out
|
||||
dec scratch
|
||||
bne check
|
||||
|
||||
;; Read failed - restore stack and exit
|
||||
reset: cpy #(read_len-1) ; anything left to restore?
|
||||
beq done ; nope, exit
|
||||
pla ; yep, clear it off the stack
|
||||
iny
|
||||
bne reset
|
||||
|
||||
;; Read succeeded - stuff it on the stack and continue
|
||||
ready: lda RDREG
|
||||
pha
|
||||
dey
|
||||
bpl rloop
|
||||
|
||||
;; Convert pushed response to ProDOS time field
|
||||
pla ; day of week (unused)
|
||||
|
||||
pla ; minute
|
||||
sta TIMELO ; -- stored as-is (TIMELO 5-0)
|
||||
|
||||
pla ; hour
|
||||
sta TIMELO+1 ; -- stored as-is (TIMELO 12-8)
|
||||
|
||||
pla ; year
|
||||
sta DATELO+1 ; -- will be shifted up by 1 (DATELO 15-9)
|
||||
|
||||
pla ; day
|
||||
and #%00011111 ; -- masked, stored as is (DATELO 4-0)
|
||||
sta DATELO
|
||||
|
||||
pla ; month
|
||||
asl a ; -- shifted up (DATELO 8-5)
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
ora DATELO ; -- merge low 5 bits
|
||||
sta DATELO
|
||||
rol DATELO+1
|
||||
|
||||
pla ; seconds (unused)
|
||||
|
||||
;; Restore prior state
|
||||
done: pla ; restore saved command state
|
||||
sta COMMAND
|
||||
plp
|
||||
rts
|
||||
.endproc
|
||||
sizeof_driver := .sizeof(driver)
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
sys_end:
|
100
inc/apple2.inc
Normal file
100
inc/apple2.inc
Normal file
|
@ -0,0 +1,100 @@
|
|||
;;; ============================================================
|
||||
;;;
|
||||
;;; More Apple II Symbols
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
;;; ============================================================
|
||||
;;; Soft Switches
|
||||
;;; ============================================================
|
||||
|
||||
;;; I/O Soft Switches / Firmware
|
||||
|
||||
RAMRDOFF := $C002
|
||||
RAMRDON := $C003
|
||||
RAMWRTOFF := $C004
|
||||
RAMWRTON := $C005
|
||||
ALTZPOFF := $C008
|
||||
ALTZPON := $C009
|
||||
|
||||
CLR80VID := $C00C
|
||||
SET80VID := $C00D
|
||||
RDALTZP := $C016
|
||||
RD80STORE := $C018
|
||||
RDPAGE2 := $C01C
|
||||
|
||||
TBCOLOR := $C022 ; IIgs: low nibble is text bg color, high is fg
|
||||
CLOCKCTL := $C034 ; IIgs: low nibble is border color
|
||||
|
||||
BANKSEL := $C073 ; Select RamWorks bank
|
||||
|
||||
ROMIN2 := $C082 ; Read ROM; no write
|
||||
ROMINWB1 := $C089 ; Read ROM; write RAM bank 1
|
||||
RWRAM1 := $C08B ; Read/write RAM bank 1
|
||||
|
||||
;;; ============================================================
|
||||
;;; I/O Registers (for Slot 2)
|
||||
;;; ============================================================
|
||||
|
||||
TDREG := $C088 + $20 ; ACIA Transmit Register (write)
|
||||
RDREG := $C088 + $20 ; ACIA Receive Register (read)
|
||||
STATUS := $C089 + $20 ; ACIA Status/Reset Register
|
||||
COMMAND := $C08A + $20 ; ACIA Command Register (read/write)
|
||||
CONTROL := $C08B + $20 ; ACIA Control Register (read/write)
|
||||
|
||||
;;; ============================================================
|
||||
;;; Other Slots
|
||||
;;; ============================================================
|
||||
|
||||
SLOT3 := $C300
|
||||
|
||||
;;; ============================================================
|
||||
;;; Monitor ROM routines
|
||||
;;; ============================================================
|
||||
|
||||
INIT := $FB2F
|
||||
SETTXT := $FB39
|
||||
TABV := $FB5B
|
||||
SETPWRC := $FB6F
|
||||
VERSION := $FBB3
|
||||
ZIDBYTE := $FBC0
|
||||
BELL1 := $FBDD
|
||||
HOME := $FC58
|
||||
CLREOL := $FC9C
|
||||
RDKEY := $FD0C
|
||||
GETLN := $FD6A ; with prompt character
|
||||
GETLN2 := $FD6F ; no prompt character
|
||||
CROUT := $FD8E
|
||||
PRBYTE := $FDDA
|
||||
COUT := $FDED
|
||||
COUT1 := $FDF0
|
||||
SETINV := $FE80
|
||||
SETNORM := $FE84
|
||||
SETKBD := $FE89
|
||||
SETVID := $FE93
|
||||
BELL := $FF3A
|
||||
|
||||
;;; ============================================================
|
||||
;;; Applesoft ROM routines
|
||||
;;; ============================================================
|
||||
;;; These require various ZP locations to be initialized.
|
||||
|
||||
LINPRNT := $ED24 ; print X,A as integer
|
||||
|
||||
;;; ============================================================
|
||||
;;; Well-known RAM locations
|
||||
;;; ============================================================
|
||||
|
||||
INPUT_BUFFER := $200
|
||||
RESETVEC := $3F2
|
||||
COL80HPOS := $57B
|
||||
|
||||
;;; ============================================================
|
||||
;;; Applesoft Zero Page Locations
|
||||
;;; ============================================================
|
||||
|
||||
TEMPPT := $52
|
||||
TEMPST := $55
|
||||
SHIFT_SIGN_EXT := $A4
|
||||
SPEEDZ := $F1
|
||||
FLASH_BIT := $F3
|
17
inc/ascii.inc
Normal file
17
inc/ascii.inc
Normal file
|
@ -0,0 +1,17 @@
|
|||
;;; ============================================================
|
||||
;;; ASCII Code Points (also used as key codes)
|
||||
;;; ============================================================
|
||||
|
||||
ASCII_BELL = $07 ; bell
|
||||
ASCII_LEFT = $08 ; left arrow
|
||||
ASCII_TAB = $09 ; tab
|
||||
ASCII_DOWN = $0A ; down arrow
|
||||
ASCII_UP = $0B ; up arrow
|
||||
ASCII_CR = $0D ; carriage return
|
||||
ASCII_RIGHT = $15 ; right arrow
|
||||
ASCII_SYN = $16 ; scroll text window up
|
||||
ASCII_ETB = $17 ; scroll text window down
|
||||
ASCII_CLEAR = $18 ; clear
|
||||
ASCII_EM = $19 ; move cursor to upper left
|
||||
ASCII_ESCAPE = $1B ; escape
|
||||
ASCII_DELETE = $7F ; delete
|
6
inc/driver_postamble.inc
Normal file
6
inc/driver_postamble.inc
Normal file
|
@ -0,0 +1,6 @@
|
|||
.ifndef BUILD_SETUP_FILE
|
||||
|
||||
poporg
|
||||
reloc_end := *
|
||||
|
||||
.endif
|
515
inc/driver_preamble.inc
Normal file
515
inc/driver_preamble.inc
Normal file
|
@ -0,0 +1,515 @@
|
|||
.define PRODUCT "Unknown"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; SYS files load at $2000; relocates self to $1000
|
||||
.org SYS_ADDR
|
||||
|
||||
.ifndef BUILD_SETUP_FILE
|
||||
dst_addr := $1000
|
||||
jmp relocate
|
||||
.else
|
||||
jmp main
|
||||
.endif
|
||||
|
||||
.byte MM, DD, YY ; version date stamp
|
||||
|
||||
.ifndef BUILD_SETUP_FILE
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Relocate this code from $2000 (.SYSTEM start location) to $1000
|
||||
;;; and start executing there. This is done so that the next .SYSTEM
|
||||
;;; file can be loaded/run at $2000.
|
||||
|
||||
.proc relocate
|
||||
src := reloc_start
|
||||
dst := dst_addr
|
||||
|
||||
ldx #(reloc_end - reloc_start + $FF) / $100 ; pages
|
||||
ldy #0
|
||||
load: lda src,y ; self-modified
|
||||
load_hi := *-1
|
||||
sta dst,y ; self-modified
|
||||
store_hi := *-1
|
||||
iny
|
||||
bne load
|
||||
inc load_hi
|
||||
inc store_hi
|
||||
dex
|
||||
bne load
|
||||
|
||||
jmp main
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Start of relocated code
|
||||
|
||||
reloc_start := *
|
||||
pushorg dst_addr
|
||||
|
||||
;;; ============================================================
|
||||
;;; Main routine
|
||||
;;; ============================================================
|
||||
|
||||
.proc main
|
||||
jsr save_chain_info
|
||||
jsr init_system
|
||||
jsr maybe_install_driver
|
||||
jmp launch_next
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Preserve state needed to chain to next file
|
||||
;;; ============================================================
|
||||
|
||||
.proc save_chain_info
|
||||
;; --------------------------------------------------
|
||||
;; Save most recent device for later, when chaining
|
||||
;; to next .SYSTEM file.
|
||||
lda DEVNUM
|
||||
sta devnum
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Identify the name of this SYS file, which should be present at
|
||||
;; $280 with or without a path prefix. Search pathname buffer
|
||||
;; backwards for '/', then copy name into `self_name`.
|
||||
|
||||
;; Find '/' (which may not be present, prefix is optional)
|
||||
ldx PATHNAME
|
||||
beq no_name
|
||||
ldy #0 ; Y = length
|
||||
: lda PATHNAME,x
|
||||
and #$7f ; ignore high bit
|
||||
cmp #'/'
|
||||
beq copy_name
|
||||
iny
|
||||
dex
|
||||
bne :-
|
||||
|
||||
;; Copy name into `self_name` buffer
|
||||
copy_name:
|
||||
cpy #0
|
||||
beq no_name
|
||||
sty self_name
|
||||
|
||||
ldx PATHNAME
|
||||
: lda PATHNAME,x
|
||||
sta self_name,y
|
||||
dex
|
||||
dey
|
||||
bne :-
|
||||
|
||||
;; Done
|
||||
rts
|
||||
|
||||
no_name:
|
||||
lda #0
|
||||
sta self_name
|
||||
rts
|
||||
.endproc
|
||||
|
||||
devnum: .byte 0
|
||||
self_name: .res 16
|
||||
|
||||
;;; ============================================================
|
||||
;;; Init system state
|
||||
;;; ============================================================
|
||||
|
||||
;;; Before installing, get the system to a known state.
|
||||
|
||||
.proc init_system
|
||||
cld
|
||||
bit ROMIN2
|
||||
|
||||
;; Update reset vector - ProDOS QUIT
|
||||
lda #<quit
|
||||
sta RESETVEC
|
||||
lda #>quit
|
||||
sta RESETVEC+1
|
||||
eor #$A5
|
||||
sta RESETVEC+2
|
||||
|
||||
;; Quit 80-column firmware
|
||||
lda #$95 ; Ctrl+U (quit 80 col firmware)
|
||||
jsr COUT
|
||||
|
||||
;; Reset I/O
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
sta CLR80COL
|
||||
jsr SETNORM
|
||||
jsr INIT
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
|
||||
;; Update System Bit Map
|
||||
ldx #BITMAP_SIZE-1
|
||||
lda #%00000001 ; protect page $BF
|
||||
: sta BITMAP,x
|
||||
lda #%00000000 ; nothing else protected until...
|
||||
dex
|
||||
bne :-
|
||||
lda #%11001111 ; ZP ($00), stack ($01), text page 1 ($04-$07)
|
||||
sta BITMAP
|
||||
|
||||
;; Determine lowercase support
|
||||
lda MACHID
|
||||
and #$88 ; IIe or IIc (or IIgs) ?
|
||||
bne :+
|
||||
lda #$DF
|
||||
sta lowercase_mask ; lower case to upper case
|
||||
|
||||
: rts
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Find and invoke the next .SYSTEM file
|
||||
;;; ============================================================
|
||||
|
||||
online_buf := $1C00
|
||||
io_buf := $1C00
|
||||
dir_buf := $2000
|
||||
block_len = $200
|
||||
|
||||
DEFINE_ON_LINE_PARAMS on_line_params,,online_buf
|
||||
DEFINE_OPEN_PARAMS open_params, PATHNAME, io_buf
|
||||
DEFINE_READ_PARAMS read_params, SYS_ADDR, SYS_LEN
|
||||
DEFINE_READ_PARAMS read_block_params, dir_buf, block_len
|
||||
DEFINE_CLOSE_PARAMS close_params
|
||||
|
||||
|
||||
.proc launch_next
|
||||
;; Read directory and look for .SYSTEM files; find this
|
||||
;; one, and invoke the following one.
|
||||
|
||||
ptr := $A5
|
||||
num := $A7
|
||||
len := $A8
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Own name found? If not, just quit
|
||||
lda self_name
|
||||
bne :+
|
||||
jmp quit
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Find name of boot device, copy into PATHNAME
|
||||
: lda devnum
|
||||
sta on_line_params::unit_num
|
||||
MLI_CALL ON_LINE, on_line_params
|
||||
bcc :+
|
||||
jmp on_error
|
||||
|
||||
: lda #'/' ; Prefix by '/'
|
||||
sta PATHNAME+1
|
||||
lda online_buf
|
||||
and #$0F ; Mask off length
|
||||
sta PATHNAME
|
||||
ldx #0 ; Copy name
|
||||
: lda online_buf+1,x
|
||||
sta PATHNAME+2,x
|
||||
inx
|
||||
cpx PATHNAME
|
||||
bne :-
|
||||
inx ; One more for '/' prefix
|
||||
stx PATHNAME
|
||||
|
||||
;; Open directory
|
||||
MLI_CALL OPEN, open_params
|
||||
bcc :+
|
||||
jmp on_error
|
||||
: lda open_params::ref_num
|
||||
sta read_block_params::ref_num
|
||||
sta close_params::ref_num
|
||||
|
||||
;; Read first "block"
|
||||
MLI_CALL READ, read_block_params
|
||||
bcc :+
|
||||
MLI_CALL CLOSE, close_params
|
||||
jmp on_error
|
||||
|
||||
;; Get sizes out of header
|
||||
: lda dir_buf + VolumeDirectoryHeader::entry_length
|
||||
sta entry_length_mod
|
||||
lda dir_buf + VolumeDirectoryHeader::entries_per_block
|
||||
sta entries_per_block_mod
|
||||
lda #1
|
||||
sta num
|
||||
|
||||
;; Set up pointers to entry
|
||||
lda #<(dir_buf + .sizeof(VolumeDirectoryHeader))
|
||||
sta ptr
|
||||
lda #>(dir_buf + .sizeof(VolumeDirectoryHeader))
|
||||
sta ptr+1
|
||||
|
||||
;; Process directory entry
|
||||
entry: ldy #FileEntry::file_type ; file_type
|
||||
lda (ptr),y
|
||||
cmp #$FF ; type=SYS
|
||||
bne next
|
||||
ldy #FileEntry::storage_type_name_length
|
||||
lda (ptr),y
|
||||
and #$30 ; regular file (not directory, pascal)
|
||||
beq next
|
||||
lda (ptr),y
|
||||
and #$0F ; name_length
|
||||
sta len
|
||||
tay
|
||||
|
||||
;; Compare suffix - is it .SYSTEM?
|
||||
ldx suffix
|
||||
: lda (ptr),y
|
||||
cmp suffix,x
|
||||
bne next
|
||||
dey
|
||||
dex
|
||||
bne :-
|
||||
|
||||
;; Yes; is it *this* .SYSTEM file?
|
||||
ldy self_name
|
||||
cpy len
|
||||
bne handle_sys_file
|
||||
: lda (ptr),y
|
||||
cmp self_name,y
|
||||
bne handle_sys_file
|
||||
dey
|
||||
bne :-
|
||||
sec
|
||||
ror found_self_flag
|
||||
|
||||
;; Move to the next entry
|
||||
next: lda ptr
|
||||
clc
|
||||
adc #$27 ; self-modified: entry_length
|
||||
entry_length_mod := *-1
|
||||
sta ptr
|
||||
bcc :+
|
||||
inc ptr+1
|
||||
: inc num
|
||||
lda num
|
||||
cmp #$0D ; self-modified: entries_per_block
|
||||
entries_per_block_mod := *-1
|
||||
bcc entry
|
||||
|
||||
;; Read next "block"
|
||||
MLI_CALL READ, read_block_params
|
||||
bcs not_found
|
||||
|
||||
;; Set up pointers to entry
|
||||
lda #0
|
||||
sta num
|
||||
lda #<(dir_buf + $04)
|
||||
sta ptr
|
||||
lda #>(dir_buf + $04)
|
||||
sta ptr+1
|
||||
jmp entry
|
||||
|
||||
;; --------------------------------------------------
|
||||
;; Found a .SYSTEM file which is not this one; invoke
|
||||
;; it if follows this one.
|
||||
handle_sys_file:
|
||||
bit found_self_flag
|
||||
bpl next
|
||||
|
||||
MLI_CALL CLOSE, close_params
|
||||
|
||||
;; Compose the path to invoke.
|
||||
ldx PATHNAME
|
||||
inx
|
||||
lda #'/'
|
||||
sta PATHNAME,x
|
||||
ldy #0
|
||||
: iny
|
||||
inx
|
||||
lda (ptr),y
|
||||
sta PATHNAME,x
|
||||
cpy len
|
||||
bcc :-
|
||||
stx PATHNAME
|
||||
|
||||
jmp invoke_system_file
|
||||
|
||||
not_found:
|
||||
MLI_CALL CLOSE, close_params
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Load/execute the system file in PATHNAME
|
||||
|
||||
.proc invoke_system_file
|
||||
MLI_CALL OPEN, open_params
|
||||
bcs on_error
|
||||
|
||||
lda open_params::ref_num
|
||||
sta read_params::ref_num
|
||||
sta close_params::ref_num
|
||||
|
||||
MLI_CALL READ, read_params
|
||||
php
|
||||
MLI_CALL CLOSE, close_params
|
||||
plp
|
||||
bcs on_error
|
||||
|
||||
jmp SYS_ADDR ; Invoke loaded SYSTEM file
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Error handler - invoked if any ProDOS error occurs.
|
||||
|
||||
.proc on_error
|
||||
pha
|
||||
jsr zstrout
|
||||
scrcode "\r\r* Disk Error $"
|
||||
.byte 0
|
||||
|
||||
pla
|
||||
jsr PRBYTE
|
||||
|
||||
jsr zstrout
|
||||
scrcode " *\r"
|
||||
.byte 0
|
||||
|
||||
bit KBDSTRB
|
||||
: lda KBD
|
||||
bpl :-
|
||||
bit KBDSTRB
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
.proc quit
|
||||
MLI_CALL QUIT, quit_params
|
||||
brk ; crash if QUIT fails
|
||||
|
||||
DEFINE_QUIT_PARAMS quit_params
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Data
|
||||
|
||||
suffix:
|
||||
PASCAL_STRING ".SYSTEM"
|
||||
|
||||
found_self_flag:
|
||||
.byte 0
|
||||
|
||||
.else
|
||||
|
||||
;;; ============================================================
|
||||
;;; Main routine
|
||||
;;; ============================================================
|
||||
|
||||
.proc main
|
||||
jsr maybe_install_driver
|
||||
|
||||
MLI_CALL QUIT, quit_params
|
||||
brk ; crash if QUIT fails
|
||||
|
||||
DEFINE_QUIT_PARAMS quit_params
|
||||
.endproc
|
||||
|
||||
.endif
|
||||
|
||||
;;; ============================================================
|
||||
;;; Common Routines
|
||||
;;; ============================================================
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Use this for logging the result of a driver. Uses `zstrout`.
|
||||
|
||||
.proc log_message
|
||||
jsr CROUT
|
||||
jsr CROUT
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: cmp #HI('a') ; lower-case?
|
||||
bcc :+
|
||||
and lowercase_mask ; make upper-case if needed
|
||||
: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
||||
lowercase_mask:
|
||||
.byte $FF ; Set to $DF on systems w/o lower-case
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; COUT a 2-digit number in A
|
||||
|
||||
.proc cout_number
|
||||
ldx #HI('0')
|
||||
cmp #10 ; >= 10?
|
||||
bcc tens
|
||||
|
||||
;; divide by 10, dividend(+'0') in x remainder in a
|
||||
: sbc #10
|
||||
inx
|
||||
cmp #10
|
||||
bcs :-
|
||||
|
||||
tens: pha
|
||||
cpx #HI('0')
|
||||
beq units
|
||||
txa
|
||||
jsr COUT
|
||||
|
||||
units: pla
|
||||
ora #HI('0')
|
||||
jsr COUT
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; COUT the current ProDOS date
|
||||
|
||||
.proc cout_date
|
||||
lda DATELO+1 ; month
|
||||
ror a
|
||||
pha
|
||||
lda DATELO
|
||||
pha
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #%00001111
|
||||
jsr cout_number
|
||||
|
||||
lda #HI('/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; day
|
||||
and #%00011111
|
||||
jsr cout_number
|
||||
|
||||
lda #HI('/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; year
|
||||
jsr cout_number
|
||||
|
||||
rts
|
||||
.endproc
|
123
inc/macros.inc
Normal file
123
inc/macros.inc
Normal file
|
@ -0,0 +1,123 @@
|
|||
;;; ============================================================
|
||||
;;; Generic Macros
|
||||
;;; ============================================================
|
||||
|
||||
.define _is_immediate(arg) (.match (.mid (0, 1, {arg}), #))
|
||||
.define _is_register(arg) (.match ({arg}, x) .or .match ({arg}, y))
|
||||
.define _is_y_register(arg) (.match ({arg}, y))
|
||||
.define _immediate_value(arg) (.right (.tcount ({arg})-1, {arg}))
|
||||
|
||||
.macro _op_lo op, arg
|
||||
.if _is_immediate {arg}
|
||||
op #<_immediate_value {arg}
|
||||
.else
|
||||
op arg
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
.macro _op_hi op, arg
|
||||
.if _is_immediate {arg}
|
||||
op #>_immediate_value {arg}
|
||||
.else
|
||||
op arg+1
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
;;; ============================================================
|
||||
;;; Temporary org change, for relocated routines
|
||||
|
||||
__pushorg_depth__ .set 0
|
||||
|
||||
.macro pushorg addr
|
||||
::__pushorg_depth__ .set ::__pushorg_depth__ + 1
|
||||
.ident(.sprintf("__pushorg_saved__%d", ::__pushorg_depth__)) := *
|
||||
.org addr
|
||||
.ident(.sprintf("__pushorg_start__%d", ::__pushorg_depth__)) := *
|
||||
.endmacro
|
||||
|
||||
.macro poporg
|
||||
.org .ident(.sprintf("__pushorg_saved__%d", ::__pushorg_depth__)) + (* - .ident(.sprintf("__pushorg_start__%d", ::__pushorg_depth__)))
|
||||
::__pushorg_depth__ .set ::__pushorg_depth__ - 1
|
||||
.endmacro
|
||||
|
||||
;;; ============================================================
|
||||
;;; Length-prefixed string
|
||||
;;;
|
||||
;;; Can include control chars by using:
|
||||
;;;
|
||||
;;; PASCAL_STRING {"abc",$0D,"def"}
|
||||
|
||||
.macro PASCAL_STRING str,res
|
||||
.local data
|
||||
.local end
|
||||
.byte end - data
|
||||
data: .byte str
|
||||
end:
|
||||
.if .paramcount > 1
|
||||
.res res - (end - data), 0
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
|
||||
;;; ============================================================
|
||||
;;; Common patterns
|
||||
|
||||
.macro copy arg1, arg2, arg3, arg4
|
||||
.if _is_register {arg2} && _is_register {arg4}
|
||||
;; indexed load/indexed store
|
||||
lda arg1,arg2
|
||||
sta arg3,arg4
|
||||
.elseif _is_register {arg2}
|
||||
;; indexed load variant (arg2 is x or y)
|
||||
lda arg1,arg2
|
||||
sta arg3
|
||||
.elseif _is_register {arg3}
|
||||
;; indexed store variant (arg3 is x or y)
|
||||
lda arg1
|
||||
sta arg2,arg3
|
||||
.else
|
||||
lda arg1
|
||||
sta arg2
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
|
||||
|
||||
;;; Copy 16-bit value
|
||||
;;; copy16 #$1111, $2222 ; immediate, absolute
|
||||
;;; copy16 $1111, $2222 ; absolute, absolute
|
||||
;;; copy16 $1111,x, $2222 ; indirect load, absolute store
|
||||
;;; copy16 $1111, $2222,x ; absolute load, indirect store
|
||||
;;; copy16 $1111,x $2222,x ; indirect load, indirect store
|
||||
;;; copy16 #$1111, $2222,x ; immediate load, indirect store
|
||||
.macro copy16 arg1, arg2, arg3, arg4
|
||||
.if _is_register {arg2} && _is_register {arg4}
|
||||
;; indexed load/indexed store
|
||||
lda arg1,arg2
|
||||
sta arg3,arg4
|
||||
lda arg1+1,arg2
|
||||
sta arg3+1,arg4
|
||||
.elseif _is_register {arg2}
|
||||
;; indexed load variant (arg2 is x or y)
|
||||
lda arg1,arg2
|
||||
sta arg3
|
||||
lda arg1+1,arg2
|
||||
sta arg3+1
|
||||
.elseif _is_register {arg3}
|
||||
;; indexed store variant (arg3 is x or y)
|
||||
_op_lo lda, {arg1}
|
||||
sta arg2,arg3
|
||||
_op_hi lda, {arg1}
|
||||
sta arg2+1,arg3
|
||||
.else
|
||||
_op_lo lda, {arg1}
|
||||
sta arg2
|
||||
_op_hi lda, {arg1}
|
||||
sta arg2+1
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
;;; Set the high bit on the passed byte
|
||||
.define HI(c) ((c)|$80)
|
478
inc/prodos.inc
Normal file
478
inc/prodos.inc
Normal file
|
@ -0,0 +1,478 @@
|
|||
;;; ============================================================
|
||||
;;;
|
||||
;;; ProDOS MLI
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
;;; Entry point / Global Page
|
||||
MLI := $BF00 ; Entry point
|
||||
DATETIME := $BF06 ; JMP to clock routine
|
||||
DEVADR := $BF10 ; Device driver addresses ($BF10-$BF2F)
|
||||
NODEV := $BF10 ; "No Device Connected" entry (slot 0)
|
||||
DEVNUM := $BF30 ; Most recent accessed device
|
||||
DEVCNT := $BF31 ; Number of on-line devices minus 1
|
||||
DEVLST := $BF32 ; Up to 14 units ($BF32-$BF3F)
|
||||
BITMAP := $BF58 ; System memory bitmap
|
||||
BITMAP_SIZE = $18 ; Bits for pages $00 to $BF
|
||||
DATELO := $BF90 ; Date lo
|
||||
DATEHI := $BF91 ; Date hi
|
||||
TIMELO := $BF92 ; Time lo
|
||||
TIMEHI := $BF93 ; Time hi
|
||||
LEVEL := $BF94 ; File level
|
||||
MACHID := $BF98 ; Machine ID
|
||||
SLTBYT := $BF99 ; '1' bits indicate rom in slot (bit#)
|
||||
IVERSION := $BFFD ; Interpreter Version
|
||||
KVERSION := $BFFF ; ProDOS Kernel Version
|
||||
|
||||
;;; Patch Locations
|
||||
SELECTOR := $D100
|
||||
|
||||
BLOCK_SIZE = $200
|
||||
|
||||
PATHNAME := $280
|
||||
SYS_ADDR := $2000 ; Load address for SYS files
|
||||
SYS_LEN = $BF00 - SYS_ADDR ; Maximum SYS file length
|
||||
|
||||
;;; ============================================================
|
||||
;;; MLI Calls
|
||||
;;; ============================================================
|
||||
|
||||
;;; Housekeeping Calls
|
||||
CREATE = $C0
|
||||
DESTROY = $C1
|
||||
RENAME = $C2
|
||||
SET_FILE_INFO = $C3
|
||||
GET_FILE_INFO = $C4
|
||||
ON_LINE = $C5
|
||||
SET_PREFIX = $C6
|
||||
GET_PREFIX = $C7
|
||||
|
||||
;;; Filing Calls
|
||||
OPEN = $C8
|
||||
NEWLINE = $C9
|
||||
READ = $CA
|
||||
WRITE = $CB
|
||||
CLOSE = $CC
|
||||
FLUSH = $CD
|
||||
SET_MARK = $CE
|
||||
GET_MARK = $CF
|
||||
SET_EOF = $D0
|
||||
GET_EOF = $D1
|
||||
SET_BUF = $D2
|
||||
GET_BUF = $D3
|
||||
|
||||
;;; System Calls
|
||||
GET_TIME = $82
|
||||
ALLOC_INTERRUPT = $40
|
||||
DEALLOC_INTERRUPT = $41
|
||||
QUIT = $65
|
||||
|
||||
;;; Direct Disk Access Commands
|
||||
READ_BLOCK = $80
|
||||
WRITE_BLOCK = $81
|
||||
|
||||
;;; ============================================================
|
||||
;;; File Types
|
||||
;;; ============================================================
|
||||
|
||||
FT_TYPELESS = $00
|
||||
FT_BAD = $01
|
||||
FT_TEXT = $04 ; ASCII Text File *
|
||||
FT_BINARY = $06 ; Generic Binary File *
|
||||
FT_GRAPHICS = $08 ; Graphics File
|
||||
FT_DIRECTORY = $0F ; Directory *
|
||||
FT_ADB = $19 ; AppleWorks Database *
|
||||
FT_AWP = $1A ; AppleWorks Word Processing *
|
||||
FT_ASP = $1B ; AppleWorks Spreadsheet *
|
||||
FT_SRC = $B0 ; IIgs system type; re-used?
|
||||
FT_S16 = $B3 ; IIgs Application Program
|
||||
FT_PAS = $EF ; Pascal Area *
|
||||
FT_CMD = $F0 ; ProDOS Command File *
|
||||
FT_INT = $FA ; Integer BASIC Program *
|
||||
FT_IVR = $FB ; Integer BASIC Variable File *
|
||||
FT_BASIC = $FC ; Applesoft BASIC Program *
|
||||
FT_VAR = $FD ; Applesoft BASIC Variable File *
|
||||
FT_REL = $FE ; EDASM/Contiki Relocatable File *
|
||||
FT_SYSTEM = $FF ; ProDOS System File *
|
||||
|
||||
;;; Types marked with * are known to BASIC.SYSTEM and have an
|
||||
;;; associated three-letter abbreviation.
|
||||
|
||||
;;; ============================================================
|
||||
;;; Access
|
||||
;;; ============================================================
|
||||
|
||||
ACCESS_DEFAULT = %11000011
|
||||
ACCESS_LOCKED = %00100001
|
||||
|
||||
;;; ============================================================
|
||||
;;; Storage Types
|
||||
;;; ============================================================
|
||||
|
||||
ST_STANDARD_FILE = $01
|
||||
ST_LINKED_DIRECTORY = $0D
|
||||
ST_VOLUME_DIRECTORY = $0F
|
||||
|
||||
;;; ============================================================
|
||||
;;; Errors
|
||||
;;; ============================================================
|
||||
|
||||
ERR_IO_ERROR = $27
|
||||
ERR_DEVICE_NOT_CONNECTED = $28
|
||||
ERR_WRITE_PROTECTED = $2B
|
||||
ERR_INVALID_PATHNAME = $40
|
||||
ERR_INVALID_REFERENCE = $43
|
||||
ERR_PATH_NOT_FOUND = $44
|
||||
ERR_VOL_NOT_FOUND = $45
|
||||
ERR_FILE_NOT_FOUND = $46
|
||||
ERR_DUPLICATE_FILENAME= $47
|
||||
ERR_OVERRUN_ERROR = $48
|
||||
ERR_VOLUME_DIR_FULL = $49
|
||||
ERR_END_OF_FILE = $4C
|
||||
ERR_ACCESS_ERROR = $4E
|
||||
ERR_DUPLICATE_VOLUME = $57
|
||||
ERR_NETWORK_ERROR = $88
|
||||
|
||||
;;; ============================================================
|
||||
;;; Directory Structures
|
||||
;;; ============================================================
|
||||
|
||||
STORAGE_TYPE_MASK = $F0
|
||||
NAME_LENGTH_MASK = $0F
|
||||
|
||||
;;; Volume Directory Header structure
|
||||
.struct VolumeDirectoryHeader
|
||||
prev_block .word
|
||||
next_block .word
|
||||
storage_type_name_length .byte
|
||||
file_name .byte 15
|
||||
reserved .byte 8
|
||||
creation_date .word
|
||||
creation_time .word
|
||||
version .byte
|
||||
min_version .byte
|
||||
access .byte
|
||||
entry_length .byte
|
||||
entries_per_block .byte
|
||||
file_count .word
|
||||
;; same through here ---------
|
||||
bit_map_pointer .word
|
||||
total_blocks .word
|
||||
.endstruct
|
||||
.assert .sizeof(VolumeDirectoryHeader) = $2B, error, "incorrect struct size"
|
||||
|
||||
;;; Subdirectory Header structure
|
||||
.struct SubdirectoryHeader
|
||||
prev_block .word
|
||||
next_block .word
|
||||
storage_type_name_length .byte
|
||||
file_name .byte 15
|
||||
reserved .byte 8
|
||||
creation_date .word
|
||||
creation_time .word
|
||||
version .byte
|
||||
min_version .byte
|
||||
access .byte
|
||||
entry_length .byte
|
||||
entries_per_block .byte
|
||||
file_count .word
|
||||
;; same through here ---------
|
||||
parent_pointer .word
|
||||
parent_entry_number .byte
|
||||
parent_entry_length .byte
|
||||
.endstruct
|
||||
.assert .sizeof(SubdirectoryHeader) = $2B, error, "incorrect struct size"
|
||||
|
||||
;; File Entry structure
|
||||
.struct FileEntry
|
||||
storage_type_name_length .byte
|
||||
file_name .byte 15
|
||||
file_type .byte
|
||||
key_pointer .word
|
||||
blocks_used .word
|
||||
eof .faraddr
|
||||
creation_date .word
|
||||
creation_time .word
|
||||
version .byte
|
||||
min_version .byte
|
||||
access .byte
|
||||
aux_type .word
|
||||
mod_date .word
|
||||
mod_time .word
|
||||
header_pointer .word
|
||||
.endstruct
|
||||
.assert .sizeof(FileEntry) = $27, error, "incorrect struct size"
|
||||
|
||||
;;; ============================================================
|
||||
;;; ProDOS Driver Protocol
|
||||
;;; ============================================================
|
||||
|
||||
;;; Addresses for command parameters
|
||||
DRIVER_COMMAND := $42
|
||||
DRIVER_UNIT_NUMBER := $43
|
||||
DRIVER_BUFFER := $44
|
||||
DRIVER_BLOCK_NUMBER := $46
|
||||
|
||||
;;; Commands
|
||||
DRIVER_COMMAND_STATUS = 0
|
||||
DRIVER_COMMAND_READ = 1
|
||||
DRIVER_COMMAND_WRITE = 2
|
||||
DRIVER_COMMAND_FORMAT = 3
|
||||
|
||||
|
||||
;;; ============================================================
|
||||
;;; Macros
|
||||
;;; ============================================================
|
||||
|
||||
.macro MLI_CALL op, addr
|
||||
jsr MLI
|
||||
.byte op
|
||||
.addr addr
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_OPEN_PARAMS name, pn, io, rn
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.if .xmatch(.string(io), "io_buffer")
|
||||
.error "Can't pass 'io_buffer' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 3
|
||||
pathname: .addr pn
|
||||
io_buffer: .addr io
|
||||
.ifnblank rn
|
||||
ref_num: .byte rn
|
||||
.else
|
||||
ref_num: .byte 0
|
||||
.endif
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_READ_PARAMS name, db, rc
|
||||
.proc name
|
||||
param_count: .byte 4
|
||||
ref_num: .byte 0
|
||||
data_buffer: .addr db
|
||||
request_count: .word rc
|
||||
trans_count: .word 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_WRITE_PARAMS name, db, rc
|
||||
.proc name
|
||||
param_count: .byte 4
|
||||
ref_num: .byte 0
|
||||
data_buffer: .addr db
|
||||
request_count: .word rc
|
||||
trans_count: .word 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_CLOSE_PARAMS name
|
||||
.proc name
|
||||
param_count: .byte 1
|
||||
ref_num: .byte 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_FLUSH_PARAMS name
|
||||
.proc name
|
||||
param_count: .byte 1
|
||||
ref_num: .byte 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_GET_FILE_INFO_PARAMS name, pn
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte $A
|
||||
pathname: .addr pn
|
||||
access: .byte 0
|
||||
file_type: .byte 0
|
||||
aux_type: .word 0
|
||||
storage_type: .byte 0
|
||||
blocks_used: .word 0
|
||||
mod_date: .word 0
|
||||
mod_time: .word 0
|
||||
create_date: .word 0
|
||||
create_time: .word 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_SET_MARK_PARAMS name, pos
|
||||
.proc name
|
||||
param_count: .byte 2
|
||||
ref_num: .byte 0
|
||||
position: .faraddr pos
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_ON_LINE_PARAMS name, un, db
|
||||
.proc name
|
||||
param_count: .byte 2
|
||||
|
||||
.ifnblank un
|
||||
unit_num: .byte un
|
||||
.else
|
||||
unit_num: .byte 0
|
||||
.endif
|
||||
|
||||
data_buffer: .addr db
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_READ_BLOCK_PARAMS name, db, bn
|
||||
.proc name
|
||||
param_count: .byte 3
|
||||
unit_num: .byte 0
|
||||
data_buffer: .addr db
|
||||
block_num: .word bn
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
|
||||
.macro DEFINE_WRITE_BLOCK_PARAMS name, db, bn
|
||||
.proc name
|
||||
param_count: .byte 3
|
||||
unit_num: .byte 0
|
||||
data_buffer: .addr db
|
||||
block_num: .word bn
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_ALLOC_INTERRUPT_PARAMS name, ic
|
||||
.proc alloc_interrupt_params
|
||||
param_count: .byte 2
|
||||
int_num: .byte 0
|
||||
int_code: .addr ic
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_DEALLOC_INTERRUPT_PARAMS name
|
||||
.proc dealloc_interrupt_params
|
||||
param_count: .byte 1
|
||||
int_num: .byte 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_QUIT_PARAMS name, ext, pathname
|
||||
.proc name
|
||||
param_count: .byte 4
|
||||
.ifnblank ext
|
||||
.byte ext
|
||||
.else
|
||||
.byte 0
|
||||
.endif
|
||||
.ifnblank pathname
|
||||
.word pathname
|
||||
.else
|
||||
.word 0
|
||||
.endif
|
||||
.byte 0
|
||||
.word 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_SET_PREFIX_PARAMS name, pn
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 1
|
||||
pathname: .addr pn
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_GET_PREFIX_PARAMS name, pn
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 1
|
||||
pathname: .addr pn
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_DESTROY_PARAMS name, pn
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 1
|
||||
pathname: .addr pn
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_CREATE_PARAMS name, pn, ac, ft, at, st
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 7
|
||||
pathname: .addr pn
|
||||
|
||||
.ifnblank ac
|
||||
access: .byte ac
|
||||
.else
|
||||
access: .byte 0
|
||||
.endif
|
||||
|
||||
.ifnblank ft
|
||||
file_type: .byte ft
|
||||
.else
|
||||
file_type: .byte 0
|
||||
.endif
|
||||
|
||||
.ifnblank at
|
||||
aux_type: .word at
|
||||
.else
|
||||
aux_type: .word 0
|
||||
.endif
|
||||
|
||||
.ifnblank st
|
||||
storage_type: .byte st
|
||||
.else
|
||||
storage_type: .byte 0
|
||||
.endif
|
||||
|
||||
create_date: .word 0
|
||||
create_time: .word 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_SET_EOF_PARAMS name, eo
|
||||
.proc name
|
||||
param_count: .byte 2
|
||||
ref_num: .byte 0
|
||||
eof: .faraddr eo
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_GET_EOF_PARAMS name
|
||||
.proc name
|
||||
param_count: .byte 2
|
||||
ref_num: .byte 0
|
||||
eof: .faraddr 0
|
||||
.endproc
|
||||
.endmacro
|
||||
|
||||
.macro DEFINE_RENAME_PARAMS name, pn, np
|
||||
.if .xmatch(.string(pn), "pathname")
|
||||
;; If 'pathname' is passed then expansion yields a circular reference.
|
||||
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
|
||||
.endif
|
||||
.proc name
|
||||
param_count: .byte 2
|
||||
pathname: .addr pn
|
||||
new_pathname: .addr np
|
||||
.endproc
|
||||
.endmacro
|
|
@ -1,718 +0,0 @@
|
|||
;;; NS.CLOCK.SYSTEM
|
||||
;;; Original by "CAP" 04/21/91
|
||||
;;; http://www.apple2.org.za/gswv/a2zine/GS.WorldView/v1999/Oct/MISC/NSC.Disk.TXT
|
||||
|
||||
;;; Modification history available at:
|
||||
;;; https://github.com/inexorabletash/cricket
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "./common.inc"
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
data_buffer = $1800
|
||||
|
||||
.define SYSTEM_SUFFIX ".SYSTEM"
|
||||
.define PRODUCT "No-Slot Clock"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.org $1000
|
||||
|
||||
;; Loaded at $2000 but relocates to $1000
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
sys_start:
|
||||
sec
|
||||
bcs relocate
|
||||
|
||||
.byte MM, DD, YY ; version date stamp
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Relocate this code from $2000 (.SYSTEM start location) to $1000
|
||||
;;; and start executing there. This is done so that the next .SYSTEM
|
||||
;;; file can be loaded/run at $2000.
|
||||
|
||||
.proc relocate
|
||||
src := SYS_ADDR
|
||||
dst := $1000
|
||||
|
||||
ldx #(sys_end - sys_start + $FF) / $100 ; pages
|
||||
ldy #0
|
||||
load: lda src,y ; self-modified
|
||||
load_hi := *-1
|
||||
sta dst,y ; self-modified
|
||||
store_hi := *-1
|
||||
iny
|
||||
bne load
|
||||
inc load_hi
|
||||
inc store_hi
|
||||
dex
|
||||
beq find_self_name ; done
|
||||
jmp load
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Identify the name of this SYS file, which should be present at
|
||||
;;; $280 with or without a path prefix. This is used when searching
|
||||
;;; for the next .SYSTEM file to execute.
|
||||
|
||||
.proc find_self_name
|
||||
;; Search pathname buffer backwards for '/', then
|
||||
;; copy name into |self_name|; this is used later
|
||||
;; to find/invoke the next .SYSTEM file.
|
||||
|
||||
;; Find '/' (which may not be present, prefix is optional)
|
||||
lda #0
|
||||
sta $A8
|
||||
ldx PATHNAME
|
||||
beq pre_install
|
||||
floop: inc $A8
|
||||
dex
|
||||
beq copy
|
||||
lda PATHNAME,x
|
||||
eor #'/'
|
||||
asl a
|
||||
bne floop
|
||||
|
||||
;; Copy name into |self_name| buffer
|
||||
copy: ldy #0
|
||||
cloop: iny
|
||||
inx
|
||||
lda PATHNAME,x
|
||||
sta self_name,y
|
||||
cpy $A8
|
||||
bcc cloop
|
||||
sty self_name
|
||||
.endproc
|
||||
;; Fall through...
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Before installing, get the system to a known state and
|
||||
;;; ensure there is not a previous clock driver installed.
|
||||
|
||||
.proc pre_install
|
||||
cld
|
||||
bit ROMIN2
|
||||
|
||||
;; Update reset vector - re-invokes this code.
|
||||
lda #<pre_install
|
||||
sta $03F2
|
||||
lda #>pre_install
|
||||
sta $03F3
|
||||
eor #$A5
|
||||
sta $03F4
|
||||
|
||||
;; Quit 80-column firmware
|
||||
lda #$95 ; Ctrl+U (quit 80 col firmware)
|
||||
jsr COUT
|
||||
|
||||
;; Reset stack
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
;; Reset I/O
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
jsr SETNORM
|
||||
jsr INIT
|
||||
|
||||
;; Update System Bit Map
|
||||
ldx #BITMAP_SIZE-1
|
||||
lda #%00000001 ; protect page $BF
|
||||
: sta BITMAP,x
|
||||
lda #%00000000 ; nothing else protected until...
|
||||
dex
|
||||
bne :-
|
||||
lda #%11001111 ; ZP ($00), stack ($01), text page 1 ($04-$07)
|
||||
sta BITMAP
|
||||
|
||||
lda MACHID
|
||||
and #$88 ; IIe or IIc (or IIgs) ?
|
||||
bne :+
|
||||
lda #$DF
|
||||
sta lowercase_mask ; lower case to upper case
|
||||
|
||||
: lda MACHID
|
||||
and #$01 ; existing clock card?
|
||||
beq detect_nsc ; nope, check for NSC
|
||||
|
||||
;; Chain with no message
|
||||
jmp launch_next_sys_file
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Detect NSC. Scan slot ROMs and main ROMs. Try reading
|
||||
;;; each location several times, and validate results before
|
||||
;;; installing driver.
|
||||
|
||||
.proc detect_nsc
|
||||
;; Preserve date/time
|
||||
ldy #3 ; copy 4 bytes
|
||||
: lda DATELO,y
|
||||
sta saved,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Check slot ROMs
|
||||
lda #>$CFFF
|
||||
ldy #<$CFFF
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #0
|
||||
sta slot
|
||||
lda #3 ; treat slot 0 as slot 3
|
||||
|
||||
sloop: ora #$C0 ; A=$Cs
|
||||
sta st1+2
|
||||
rloop: sta ld1+2
|
||||
sta ld2+2
|
||||
sta st2+2
|
||||
|
||||
lda #3 ; 3 tries - need valid results each time
|
||||
sta tries
|
||||
try: jsr driver ; try reading date/time
|
||||
lda DATELO+1 ; check result
|
||||
ror a
|
||||
lda DATELO
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #$0F
|
||||
beq next
|
||||
cmp #13 ; month
|
||||
bcs next
|
||||
lda DATELO
|
||||
and #$1F
|
||||
beq next
|
||||
cmp #32 ; day
|
||||
bcs next
|
||||
lda TIMELO+1
|
||||
cmp #24 ; hours
|
||||
bcs next
|
||||
lda TIMELO
|
||||
cmp #60 ; minutes
|
||||
bcs next
|
||||
dec tries
|
||||
bne try
|
||||
beq install_driver ; all tries look valid
|
||||
next: inc slot
|
||||
lda slot
|
||||
cmp #8
|
||||
bcc sloop ; next slot
|
||||
bne not_found
|
||||
|
||||
;; Not found in slot ROM, try main ROMs ???
|
||||
lda #>$C015
|
||||
ldy #<$C015
|
||||
sta ld4+2
|
||||
sty ld4+1
|
||||
ldy #$07
|
||||
sta st1+2
|
||||
sty st1+1
|
||||
dey
|
||||
sta st4+2
|
||||
sty st4+1
|
||||
lda #>$C800
|
||||
bne rloop
|
||||
|
||||
;; Restore date/time
|
||||
not_found:
|
||||
ldy #3
|
||||
: lda saved,y
|
||||
sta DATELO,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Show failure message
|
||||
jsr MON_HOME
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, PRODUCT, " - Not Found."
|
||||
jmp launch_next_sys_file
|
||||
|
||||
saved: .byte 0, 0, 0, 0
|
||||
tries: .byte 3
|
||||
slot: .byte 0
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Install NSC Driver. Copy into address at DATETIME vector,
|
||||
;;; update the vector and update MACHID bits to signal a clock
|
||||
;;; is present.
|
||||
|
||||
.proc install_driver
|
||||
ptr := $A5
|
||||
|
||||
lda DATETIME+1
|
||||
sta ptr
|
||||
clc
|
||||
adc #(unlock - driver - 1)
|
||||
sta ld3+1
|
||||
lda DATETIME+2
|
||||
sta ptr+1
|
||||
adc #0
|
||||
sta ld3+2
|
||||
lda RWRAM1
|
||||
lda RWRAM1
|
||||
ldy #sizeof_driver-1
|
||||
|
||||
loop: lda driver,y
|
||||
sta (ptr),y
|
||||
dey
|
||||
bpl loop
|
||||
|
||||
;; Set the "Recognizable Clock Card" bit
|
||||
lda MACHID
|
||||
ora #$01
|
||||
sta MACHID
|
||||
|
||||
lda #OPC_JMP_abs
|
||||
sta DATETIME
|
||||
|
||||
;; Invoke the driver to init the time
|
||||
jsr DATETIME
|
||||
|
||||
;; Display success message
|
||||
bit ROMIN2
|
||||
jsr MON_HOME
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, PRODUCT, " - Installed "
|
||||
|
||||
;; Display the current date
|
||||
lda DATELO+1 ; month
|
||||
ror a
|
||||
pha
|
||||
lda DATELO
|
||||
pha
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
rol a
|
||||
and #%00001111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; day
|
||||
and #%00011111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; year
|
||||
jsr cout_number
|
||||
jsr CROUT
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Find and invoke the next .SYSTEM file
|
||||
|
||||
.proc launch_next_sys_file
|
||||
;; Update reset vector - now terminates.
|
||||
lda #<quit
|
||||
sta $03F2
|
||||
lda #>quit
|
||||
sta $03F3
|
||||
eor #$A5
|
||||
sta $03F4
|
||||
|
||||
ptr := $A5
|
||||
num := $A7
|
||||
len := $A8
|
||||
|
||||
lda DEVNUM ; stick with most recent device
|
||||
sta read_block_params_unit_num
|
||||
jsr read_block
|
||||
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::entry_length
|
||||
sta entry_length_mod
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::entries_per_block
|
||||
sta entries_per_block_mod
|
||||
lda #1
|
||||
sta num
|
||||
|
||||
lda #<(data_buffer + VolumeDirectoryBlockHeader::header_length)
|
||||
sta ptr
|
||||
lda #>(data_buffer + VolumeDirectoryBlockHeader::header_length)
|
||||
sta ptr+1
|
||||
|
||||
;; Process directory entry
|
||||
entry: ldy #FileEntry::file_type ; file_type
|
||||
lda (ptr),y
|
||||
cmp #$FF ; type=SYS
|
||||
bne next
|
||||
ldy #FileEntry::storage_type
|
||||
lda (ptr),y
|
||||
and #$30 ; regular file (not directory, pascal)
|
||||
beq next
|
||||
lda (ptr),y
|
||||
and #$0F ; name_length
|
||||
sta len
|
||||
tay
|
||||
|
||||
;; Compare suffix - is it .SYSTEM?
|
||||
ldx #.strlen(SYSTEM_SUFFIX)-1
|
||||
: lda (ptr),y
|
||||
cmp suffix,x
|
||||
bne next
|
||||
dey
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
;; Yes; is it *this* .SYSTEM file?
|
||||
ldy self_name
|
||||
cpy len
|
||||
bne handle_sys_file
|
||||
: lda (ptr),y
|
||||
cmp self_name,y
|
||||
bne handle_sys_file
|
||||
dey
|
||||
bne :-
|
||||
sec
|
||||
ror found_self_flag
|
||||
|
||||
;; Move to the next entry
|
||||
next: lda ptr
|
||||
clc
|
||||
adc #$27 ; self-modified: entry_length
|
||||
entry_length_mod := *-1
|
||||
sta ptr
|
||||
bcc :+
|
||||
inc ptr+1
|
||||
: inc num
|
||||
lda num
|
||||
cmp #$0D ; self-modified: entries_per_block
|
||||
entries_per_block_mod := *-1
|
||||
bcc entry
|
||||
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::next_block
|
||||
sta read_block_params_block_num
|
||||
lda data_buffer + VolumeDirectoryBlockHeader::next_block + 1
|
||||
sta read_block_params_block_num+1
|
||||
ora read_block_params_block_num
|
||||
beq not_found ; last block has next=0
|
||||
jsr read_block
|
||||
lda #0
|
||||
sta num
|
||||
lda #<(data_buffer + $04)
|
||||
sta ptr
|
||||
lda #>(data_buffer + $04)
|
||||
sta ptr+1
|
||||
jmp entry
|
||||
|
||||
;; Found a .SYSTEM file which is not this one; invoke
|
||||
;; it if follows this one.
|
||||
handle_sys_file:
|
||||
bit found_self_flag
|
||||
bpl next
|
||||
|
||||
;; Compose the path to invoke. First walk self path
|
||||
;; backwards to '/'.
|
||||
ldx PATHNAME
|
||||
beq append
|
||||
: dex
|
||||
beq append
|
||||
lda PATHNAME,x
|
||||
eor #'/'
|
||||
asl a
|
||||
bne :-
|
||||
|
||||
;; Now append name of found file.
|
||||
append: ldy #0
|
||||
: iny
|
||||
inx
|
||||
lda (ptr),y
|
||||
sta PATHNAME,x
|
||||
cpy len
|
||||
bcc :-
|
||||
stx PATHNAME
|
||||
jmp invoke_system_file
|
||||
|
||||
not_found:
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, "* Unable to find next '.SYSTEM' file *", CR
|
||||
bit KBDSTRB
|
||||
: lda KBD
|
||||
bpl :-
|
||||
bit KBDSTRB
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Output a high-ascii, null-terminated string.
|
||||
;;; String immediately follows the JSR.
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: cmp #(HI 'a') ; lower-case?
|
||||
bcc :+
|
||||
and lowercase_mask ; make upper-case if needed
|
||||
: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; COUT a 2-digit number in A
|
||||
|
||||
.proc cout_number
|
||||
ldx #(HI '0')
|
||||
cmp #10 ; >= 10?
|
||||
bcc tens
|
||||
|
||||
;; divide by 10, dividend(+'0') in x remainder in a
|
||||
: sbc #10
|
||||
inx
|
||||
cmp #10
|
||||
bcs :-
|
||||
|
||||
tens: pha
|
||||
cpx #(HI '0')
|
||||
beq units
|
||||
txa
|
||||
jsr COUT
|
||||
|
||||
units: pla
|
||||
ora #(HI '0')
|
||||
jsr COUT
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
lowercase_mask:
|
||||
.byte $FF ; Set to $DF on systems w/o lower-case
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Invoke ProDOS QUIT routine.
|
||||
|
||||
.proc quit
|
||||
PRODOS_CALL MLI_QUIT, quit_params
|
||||
.byte 0 ; crash if QUIT fails
|
||||
rts
|
||||
.proc quit_params
|
||||
.byte 4 ; param_count
|
||||
.byte 0 ; quit_type
|
||||
.word 0000 ; reserved
|
||||
.byte 0 ; reserved
|
||||
.word 0000 ; reserved
|
||||
.endproc
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Read a disk block.
|
||||
|
||||
.proc read_block
|
||||
PRODOS_CALL MLI_READ_BLOCK, read_block_params
|
||||
bcs on_error
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.proc read_block_params
|
||||
.byte 3 ; param_count
|
||||
unit_num: .byte $60 ; unit_num
|
||||
.addr data_buffer ; data_buffer
|
||||
block_num: .word 2 ; block_num - block 2 is volume directory
|
||||
.endproc
|
||||
read_block_params_unit_num := read_block_params::unit_num
|
||||
read_block_params_block_num := read_block_params::block_num
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Load/execute the system file in PATHNAME
|
||||
|
||||
.proc invoke_system_file
|
||||
PRODOS_CALL MLI_OPEN, open_params
|
||||
bcs on_error
|
||||
|
||||
lda open_params_ref_num
|
||||
sta read_params_ref_num
|
||||
|
||||
PRODOS_CALL MLI_READ, read_params
|
||||
bcs on_error
|
||||
|
||||
PRODOS_CALL MLI_CLOSE, close_params
|
||||
bcs on_error
|
||||
|
||||
jmp SYS_ADDR ; Invoke loaded SYSTEM file
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Error handler - invoked if any ProDOS error occurs.
|
||||
|
||||
.proc on_error
|
||||
pha
|
||||
jsr zstrout
|
||||
HIASCIIZ CR, CR, CR, "** Disk Error $"
|
||||
pla
|
||||
jsr PRBYTE
|
||||
jsr zstrout
|
||||
HIASCIIZ " **", CR
|
||||
bit KBDSTRB
|
||||
: lda KBD
|
||||
bpl :-
|
||||
bit KBDSTRB
|
||||
jmp quit
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc open_params
|
||||
.byte 3 ; param_count
|
||||
.addr PATHNAME ; pathname
|
||||
.addr data_buffer ; io_buffer
|
||||
ref_num:.byte 1 ; ref_num
|
||||
.endproc
|
||||
open_params_ref_num := open_params::ref_num
|
||||
|
||||
.proc read_params
|
||||
.byte 4 ; param_count
|
||||
ref_num:.byte 1 ; ref_num
|
||||
.addr SYS_ADDR ; data_buffer
|
||||
.word MAX_DW ; request_count
|
||||
.word 0 ; trans_count
|
||||
.endproc
|
||||
read_params_ref_num := read_params::ref_num
|
||||
|
||||
.proc close_params
|
||||
.byte 1 ; param_count
|
||||
ref_num:.byte 0 ; ref_num
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
found_self_flag:
|
||||
.byte 0
|
||||
|
||||
suffix: .byte SYSTEM_SUFFIX
|
||||
|
||||
self_name:
|
||||
PASCAL_STRING "NS.CLOCK.SYSTEM"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; NSC driver - modified as needed and copied into ProDOS
|
||||
|
||||
driver:
|
||||
php
|
||||
sei
|
||||
ld4: lda $CFFF ; self-modified
|
||||
pha
|
||||
st1: sta $C300 ; self-modified
|
||||
ld1: lda $C304 ; self-modified
|
||||
ldx #8
|
||||
|
||||
;; Unlock the NSC by bit-banging.
|
||||
uloop:
|
||||
ld3: lda unlock-1,x ; self-modified
|
||||
sec
|
||||
ror a ; a bit at a time
|
||||
: pha
|
||||
lda #0
|
||||
rol a
|
||||
tay
|
||||
ld2: lda $C300,y ; self-modified
|
||||
pla
|
||||
lsr a
|
||||
bne :-
|
||||
dex
|
||||
bne uloop
|
||||
|
||||
;; Read 8 bytes * 8 bits of clock data into $200...$207
|
||||
ldx #8
|
||||
bloop: ldy #8
|
||||
st2:
|
||||
: lda $C304 ; self-modified
|
||||
ror a
|
||||
ror $01FF,x
|
||||
dey
|
||||
bne :-
|
||||
lda $01FF,x ; got 8 bits
|
||||
|
||||
lsr a ; BCD to binary
|
||||
lsr a ; shift out tens
|
||||
lsr a
|
||||
lsr a
|
||||
tay
|
||||
beq donebcd
|
||||
lda $01FF,x
|
||||
and #$0F ; mask out units
|
||||
clc
|
||||
: adc #10 ; and add tens as needed
|
||||
dey
|
||||
bne :-
|
||||
sta $01FF,x
|
||||
donebcd:
|
||||
dex
|
||||
bne bloop
|
||||
|
||||
;; Now $200...$207 is y/m/d/w/H/M/S/f
|
||||
|
||||
;; Update ProDOS date/time.
|
||||
lda $0204 ; hour
|
||||
sta TIMELO+1
|
||||
|
||||
lda $0205 ; minute
|
||||
sta TIMELO
|
||||
|
||||
lda $0201 ; month
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
|
||||
ora $0202 ; day
|
||||
sta DATELO
|
||||
|
||||
lda $0200 ; year
|
||||
rol a
|
||||
sta DATELO+1
|
||||
|
||||
pla
|
||||
bmi done
|
||||
st4: sta $CFFF ; self-modified
|
||||
done: plp
|
||||
rts
|
||||
|
||||
unlock:
|
||||
;; NSC unlock sequence
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
.byte $5C, $A3, $3A, $C5
|
||||
.byte $00
|
||||
|
||||
sizeof_driver := * - driver
|
||||
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
sys_end:
|
59
package.sh
Executable file
59
package.sh
Executable file
|
@ -0,0 +1,59 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Use Cadius to create a disk image for distribution
|
||||
# https://github.com/mach-kernel/cadius
|
||||
|
||||
set -e
|
||||
|
||||
PACKDIR=$(mktemp -d)
|
||||
IMGFILE="prodos-drivers.po"
|
||||
VOLNAME="drivers"
|
||||
|
||||
rm -f "$IMGFILE"
|
||||
cadius CREATEVOLUME "$IMGFILE" "$VOLNAME" 140KB --no-case-bits --quiet
|
||||
cadius CREATEFOLDER "$IMGFILE" "/$VOLNAME/SETUPS" --no-case-bits --quiet
|
||||
|
||||
add_file () {
|
||||
cp "$1" "$PACKDIR/$2"
|
||||
cadius ADDFILE "$IMGFILE" "$3" "$PACKDIR/$2" --no-case-bits --quiet
|
||||
}
|
||||
|
||||
# Drivers
|
||||
|
||||
for file in \
|
||||
"bbb" "buhbye" "bye" "selector" \
|
||||
"clock" "cricket" "dclock" "fn.clock" "ns.clock" "romxrtc" \
|
||||
"ram.drv" \
|
||||
"zipchip" \
|
||||
"me.first" "pause"; do
|
||||
add_file "out/$file.system.SYS" "$file.system#FF0000" "/$VOLNAME"
|
||||
add_file "out/$file.setup.SYS" "$file.setup#FF0000" "/$VOLNAME/SETUPS"
|
||||
done
|
||||
add_file "out/setup.system.SYS" "setup.system#FF0000" "/$VOLNAME"
|
||||
add_file "out/quit.system.SYS" "quit.system#FF0000" "/$VOLNAME"
|
||||
|
||||
# Utilities
|
||||
|
||||
add_file "out/date.BIN" "date#062000" "/$VOLNAME"
|
||||
|
||||
cadius CREATEFOLDER "$IMGFILE" "/$VOLNAME/CRICKET.UTIL" --no-case-bits --quiet
|
||||
add_file "out/cricket.util/set.datetime.BIN" "set.datetime#062000" "/$VOLNAME/CRICKET.UTIL"
|
||||
add_file "out/cricket.util/set.date.BIN" "set.date#062000" "/$VOLNAME/CRICKET.UTIL"
|
||||
add_file "out/cricket.util/set.time.BIN" "set.time#062000" "/$VOLNAME/CRICKET.UTIL"
|
||||
add_file "out/cricket.util/test.BIN" "test#062000" "/$VOLNAME/CRICKET.UTIL"
|
||||
|
||||
cadius CREATEFOLDER "$IMGFILE" "/$VOLNAME/NSCLOCK.UTIL" --no-case-bits --quiet
|
||||
add_file "out/nsclock.util/set.datetime.BIN" "set.datetime#062000" "/$VOLNAME/NSCLOCK.UTIL"
|
||||
|
||||
|
||||
|
||||
cadius CREATEFOLDER "$IMGFILE" "/$VOLNAME/TEXTCOLORS" --no-case-bits --quiet
|
||||
cadius CREATEFOLDER "$IMGFILE" "/$VOLNAME/SETUPS/TEXTCOLORS" --no-case-bits --quiet
|
||||
for file in a2green bw deepblue gray gsblue mint pink wb; do
|
||||
add_file "out/${file}.system.SYS" "${file}.system#FF0000" "/$VOLNAME/TEXTCOLORS"
|
||||
add_file "out/${file}.setup.SYS" "${file}.setup#FF0000" "/$VOLNAME/TEXTCOLORS/SETUPS"
|
||||
done
|
||||
|
||||
rm -r "$PACKDIR"
|
||||
|
||||
cadius CATALOG "$IMGFILE"
|
43
ram.drv/Makefile
Normal file
43
ram.drv/Makefile
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/ram.drv.system.SYS \
|
||||
$(OUTDIR)/ram.drv.setup.SYS
|
||||
|
||||
export LOG_SUCCESS=1
|
||||
export LOG_FAILURE=1
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY) \
|
||||
-D LOG_SUCCESS=$(LOG_SUCCESS) -D LOG_FAILURE=$(LOG_FAILURE)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
15
ram.drv/README.md
Normal file
15
ram.drv/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Disassembly of Glen E. Bredon's `RAM.DRV.SYSTEM` for Apple II ProDOS
|
||||
|
||||
This was started before realizing what the origin of the `RAM.SYSTEM`
|
||||
found on a MouseDesk 2.0 disk image file was.
|
||||
|
||||
There is a more complete diassembly with commentary at:
|
||||
|
||||
http://boutillon.free.fr/Underground/Outils/Ram_Drv_System/Ram_Drv_System.html
|
||||
|
||||
## Project Details
|
||||
|
||||
* The `orig` branch compiles to to match the original.
|
||||
* The `master` branch has additions, including:
|
||||
* Chains to next `.SYSTEM` file in dir order (not hard coded)
|
||||
* Chains to next `.SYSTEM` file on non-block devices (e.g. file shares)
|
BIN
ram.drv/orig/RAM.SYSTEM.SYS
Normal file
BIN
ram.drv/orig/RAM.SYSTEM.SYS
Normal file
Binary file not shown.
715
ram.drv/ram.drv.system.s
Normal file
715
ram.drv/ram.drv.system.s
Normal file
|
@ -0,0 +1,715 @@
|
|||
;;; Disassembly of "RAM.SYSTEM" found on Mouse Desk 2.0 images
|
||||
;;; Based on Glen E. Bredon's "RAM.DRV.SYSTEM"
|
||||
;;; Some details c/o http://boutillon.free.fr/Underground/Outils/Ram_Drv_System/Ram_Drv_System.html
|
||||
;;;
|
||||
;;; Modifications:
|
||||
;;; * Chain to next .SYSTEM file dynamically
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
.include "opcodes.inc"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ============================================================
|
||||
;;;
|
||||
;;; Driver Installer
|
||||
;;;
|
||||
;;; ============================================================
|
||||
|
||||
;;; ============================================================
|
||||
;;; Configuration Parameters
|
||||
|
||||
.undef PRODUCT
|
||||
.define PRODUCT "RAMWorks RAM Disk"
|
||||
|
||||
zp_sig_addr := $06
|
||||
|
||||
zpproc_addr := $B0
|
||||
zpproc_relay_addr := $2D0
|
||||
|
||||
driver_target := $FF00 ; Install location in ProDOS
|
||||
|
||||
|
||||
kMaxUsableBanks = 24 ; Why is this capped so low???
|
||||
; (driver has room for another ~20?)
|
||||
|
||||
banks_to_reserve: .byte 0 ; banks to reserve (e.g. for AppleWorks)
|
||||
unitnum: .byte $03 ; S3D1; could be $B for S3D2
|
||||
|
||||
;;; ============================================================
|
||||
;;; Install the driver
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
sta CLR80COL
|
||||
ldy #0
|
||||
sty BANKSEL
|
||||
sta ALTZPON ; Use ZP to probe banks
|
||||
|
||||
;; Clear map1 / map2 (256 bytes) to $FF
|
||||
lda #$FF
|
||||
: sta map1,y
|
||||
iny
|
||||
bne :-
|
||||
|
||||
;; Stash first two bytes of each bank (128 possible banks)
|
||||
: sty BANKSEL
|
||||
lda $00
|
||||
sta stash_00,y
|
||||
lda $01
|
||||
sta stash_01,y
|
||||
iny
|
||||
bpl :-
|
||||
dey
|
||||
|
||||
;; Write bank num/complement at $0/$1
|
||||
: sty BANKSEL
|
||||
sty $00
|
||||
tya
|
||||
eor #$FF
|
||||
sta $01
|
||||
dey
|
||||
bne :-
|
||||
|
||||
;; Y = 0
|
||||
|
||||
;; Reset signature bytes on main/aux banks
|
||||
sty BANKSEL
|
||||
sty $00
|
||||
sty $01
|
||||
sta ALTZPOFF
|
||||
sty $00
|
||||
sty $01
|
||||
sta ALTZPON
|
||||
|
||||
lda banks_to_reserve
|
||||
sta reserved_banks
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
;; Copy into every bank
|
||||
ldy #1
|
||||
bank_loop:
|
||||
;; Check bank for signature bytes (bank num/complement at $0/$1)
|
||||
sty BANKSEL
|
||||
cpy $00
|
||||
bne next_bank
|
||||
tya
|
||||
eor #$FF
|
||||
eor $01
|
||||
bne next_bank
|
||||
cpy $00 ; Bank 0 (aux) is reserved for 128k apps
|
||||
bne next_bank
|
||||
|
||||
;; Flag as available in map2
|
||||
;; (map2,N = N if available, $FF otherwise)
|
||||
tya
|
||||
sta map2,y
|
||||
|
||||
;; Skip over reserved banks, then start storing them in the map
|
||||
ldx reserved_banks
|
||||
bne :+
|
||||
sta first_used_bank
|
||||
: dec reserved_banks
|
||||
bpl next_bank
|
||||
sta map1,y
|
||||
;; (map1,N = N if available, $FF otherwise - also???)
|
||||
|
||||
;; Copy helper proc into bank's ZP
|
||||
ldx #sizeof_zpproc
|
||||
: lda zpproc-1,x
|
||||
sta zpproc_addr-1,x
|
||||
dex
|
||||
bne :-
|
||||
|
||||
next_bank:
|
||||
iny
|
||||
bpl bank_loop
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
;; Y = $80
|
||||
|
||||
;; Restore stashed $0/$1 bytes of back
|
||||
;; (except first, in first_used_bank ???)
|
||||
loop0: lda map2-1,y
|
||||
bmi :+
|
||||
cmp first_used_bank
|
||||
beq :+
|
||||
sta BANKSEL
|
||||
lda stash_00-1,y
|
||||
sta $00
|
||||
lda stash_01-1,y
|
||||
sta $01
|
||||
: dey
|
||||
bne loop0
|
||||
|
||||
;; Y = 0
|
||||
sty BANKSEL
|
||||
sty $00
|
||||
|
||||
;; Count number of available banks, and populate
|
||||
;; driver_bank_list with list of banks.
|
||||
ldx #$FF
|
||||
loop1: inx
|
||||
cpx #kMaxUsableBanks
|
||||
bcs break
|
||||
loop2: iny
|
||||
bmi break
|
||||
lda map1,y
|
||||
bmi loop2
|
||||
sta driver_bank_list,x
|
||||
bpl loop1
|
||||
break:
|
||||
;; Patch driver with block-specific data
|
||||
;; X = number of available banks
|
||||
|
||||
;; Compute number of blocks
|
||||
txa
|
||||
lsr a
|
||||
sta vol_dir_header+VolumeDirectoryHeader::total_blocks+1
|
||||
ror vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
|
||||
stx driver_block_x ; num banks
|
||||
dex ; -1
|
||||
stx num_banks_minus_one
|
||||
|
||||
bmi fail ; 0 banks? give up.
|
||||
|
||||
lda vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
sec
|
||||
sbc driver_block_x
|
||||
and #$F8
|
||||
sta vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
sta driver_blocks_lo
|
||||
bcs :+
|
||||
dec vol_dir_header+VolumeDirectoryHeader::total_blocks+1
|
||||
: lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
|
||||
sta driver_blocks_hi
|
||||
|
||||
lda driver_bank_list
|
||||
sta BANKSEL
|
||||
lda $00
|
||||
beq fail
|
||||
|
||||
;; Check for ZP signature - if not found, set it and install.
|
||||
ldx #sig_len-1
|
||||
: lda sig,x
|
||||
cmp zp_sig_addr,x
|
||||
bne set_sig
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
bit BUTN1 ; escape hatch in case of loop ???
|
||||
bmi L21F0
|
||||
jmp do_install
|
||||
|
||||
fail: jmp install_failure
|
||||
|
||||
sloop: lda sig,x
|
||||
set_sig:
|
||||
sta zp_sig_addr,x
|
||||
dex
|
||||
bpl sloop
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
;;; Prepare key blocks in
|
||||
|
||||
L21F0: sta ALTZPOFF
|
||||
|
||||
;; Stamp current date/time into vol_dir_header
|
||||
ldy #3
|
||||
: lda DATELO,y
|
||||
sta vol_dir_header+VolumeDirectoryHeader::creation_date,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Fill pages $06-$0F with 00-FF
|
||||
sta RAMWRTON
|
||||
iny
|
||||
tya
|
||||
: sta $0600,y ; Block 2 - volume dir
|
||||
sta $0700,y
|
||||
sta $0800,y ; Block 3 - volume dir
|
||||
sta $0900,y
|
||||
sta $0A00,y ; Block 4 - volume dir
|
||||
sta $0B00,y
|
||||
sta $0C00,y ; Block 5 - volume dir
|
||||
sta $0D00,y
|
||||
sta $0E00,y ; Block 6 - volume bitmap
|
||||
sta $0F00,y
|
||||
iny
|
||||
bne :-
|
||||
|
||||
;; Copy vol_dir_header into page $06
|
||||
ldy #.sizeof(VolumeDirectoryHeader)-1
|
||||
: lda vol_dir_header,y
|
||||
sta $0600,y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
ldy #$02
|
||||
sty $0800
|
||||
iny
|
||||
sty $0A00
|
||||
iny
|
||||
sty $0C00
|
||||
sty $0802
|
||||
iny
|
||||
sty $0A02
|
||||
|
||||
ptr := $3C
|
||||
lda vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
sta ptr
|
||||
lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
|
||||
lsr a
|
||||
ror ptr
|
||||
lsr a
|
||||
ror ptr
|
||||
lsr a
|
||||
ror ptr
|
||||
clc
|
||||
adc #$0E
|
||||
sta ptr+1
|
||||
|
||||
ldy #0
|
||||
tya
|
||||
: sta (ptr),y
|
||||
lda ptr
|
||||
sec
|
||||
sbc #1
|
||||
sta ptr
|
||||
lda #$FF
|
||||
bcs :-
|
||||
dec ptr+1
|
||||
ldx ptr+1
|
||||
cpx #$0E
|
||||
bcs :-
|
||||
lda #$01
|
||||
sta $0E00
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
do_install:
|
||||
lda #0
|
||||
sta RAMWRTOFF
|
||||
sta ALTZPOFF
|
||||
sta BANKSEL
|
||||
bit LCBANK1
|
||||
bit LCBANK1
|
||||
|
||||
lda #OPC_CLD ; signature
|
||||
cmp driver_target
|
||||
beq copy_driver
|
||||
sta ALTZPON ; Maybe in AUX?
|
||||
cmp driver_target
|
||||
beq copy_driver
|
||||
cmp $DE00 ; ???
|
||||
beq copy_driver
|
||||
sta ALTZPOFF
|
||||
|
||||
;; Copy driver into place
|
||||
copy_driver:
|
||||
ldy #0
|
||||
: lda driver_src,y
|
||||
sta driver_target,y
|
||||
iny
|
||||
cpy #sizeof_driver
|
||||
bcc :-
|
||||
|
||||
;; Check if unitnum already has a device
|
||||
ldy DEVCNT
|
||||
: lda DEVLST,y
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
cmp unitnum
|
||||
beq install_device
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
;; Otherwise, insert at end. ProDOS resolves names to devices
|
||||
;; from last to first, so fast RAM disks should go last.
|
||||
inc DEVCNT
|
||||
ldy DEVCNT
|
||||
|
||||
;; Install device in ProDOS via DEVLST/DEVADR.
|
||||
;; (Y has index in DEVLST)
|
||||
install_device:
|
||||
lda unitnum
|
||||
asl a
|
||||
tax
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sta on_line_params+1 ; unit_number
|
||||
ora #$0E ; $3E - signature byte used by DeskTop
|
||||
sta DEVLST,y
|
||||
copy16 #(driver_target+1), DEVADR,x
|
||||
|
||||
;; Did we install into S3D2?
|
||||
lda unitnum
|
||||
cmp #$0B ; Slot 3 Drive 2
|
||||
beq finish
|
||||
|
||||
;; No, so uninstall S3D2 (regular /RAM)
|
||||
ldy DEVCNT
|
||||
: lda DEVLST,y
|
||||
and #$F0
|
||||
cmp #$B0 ; Slot 3 drive 2 i.e. normal /RAM
|
||||
beq found
|
||||
dey
|
||||
bpl :-
|
||||
bmi finish ; always
|
||||
|
||||
;; Actually remove from DEVLST
|
||||
slot3d2_devadr := DEVADR + $10 + 3*2
|
||||
found: ldx slot3d2_devadr + 1
|
||||
inx
|
||||
bne finish
|
||||
: copy DEVLST+1,y, DEVLST,y
|
||||
iny
|
||||
cpy DEVCNT
|
||||
bcc :-
|
||||
beq :-
|
||||
dec DEVCNT
|
||||
copy16 NODEV, slot3d2_devadr ; clear driver
|
||||
|
||||
finish: bit ROMIN2
|
||||
MLI_CALL ON_LINE, on_line_params
|
||||
ldx #$00
|
||||
lda on_line_params_buffer
|
||||
ora L239F
|
||||
bne install_success
|
||||
bcc install_success
|
||||
copy #$FF, L239F
|
||||
sta ALTZPON
|
||||
copy driver_bank_list, BANKSEL
|
||||
stx $06
|
||||
stx BANKSEL
|
||||
stx vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
jmp maybe_install_driver ; retry???
|
||||
|
||||
install_success:
|
||||
sta ALTZPOFF
|
||||
|
||||
.if ::LOG_SUCCESS
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - "
|
||||
.byte 0
|
||||
.endif ; ::LOG_SUCCESS
|
||||
|
||||
;; Initialize Applesoft zero page locations required by LINPRNT
|
||||
copy #0, SHIFT_SIGN_EXT ; required by FP routines
|
||||
copy #TEMPST, TEMPPT ; string descriptor pointer
|
||||
copy #1, SPEEDZ ; output delay
|
||||
copy #0, FLASH_BIT ; character set mask
|
||||
|
||||
ldx vol_dir_header+VolumeDirectoryHeader::total_blocks
|
||||
lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
|
||||
jsr LINPRNT
|
||||
|
||||
jsr zstrout
|
||||
scrcode " Blocks"
|
||||
.byte 0
|
||||
|
||||
rts
|
||||
|
||||
install_failure:
|
||||
sta ALTZPOFF
|
||||
|
||||
.if ::LOG_FAILURE
|
||||
jsr log_message
|
||||
scrcode PRODUCT, " - Not Found"
|
||||
.byte 0
|
||||
.endif ; ::LOG_FAILURE
|
||||
|
||||
rts
|
||||
|
||||
;;; ============================================================
|
||||
;;; Installed on zero page of each bank at $B0
|
||||
|
||||
.proc zpproc
|
||||
pushorg ::zpproc_addr
|
||||
|
||||
sta $E0 ; dst1 hi
|
||||
bcs :+
|
||||
sty $E0 ; dst1 hi
|
||||
tay
|
||||
: lda #$00
|
||||
sta RAMWRTON
|
||||
bcc :+
|
||||
txa
|
||||
ldx #$00
|
||||
sta RAMWRTOFF
|
||||
sta RAMRDON
|
||||
|
||||
;; One block = two pages
|
||||
: sty $DD ; src1 hi
|
||||
iny
|
||||
sty $E3 ; src2 hi
|
||||
|
||||
sta $DF ; dst1 lo
|
||||
sta $E5 ; dst2 lo
|
||||
|
||||
stx $DC ; src1 lo
|
||||
stx $E2 ; src2 lo
|
||||
|
||||
ldy $E0 ; dst1 hi
|
||||
iny
|
||||
sty $E6 ; dst2 hi
|
||||
|
||||
ldy #$00
|
||||
: lda $1000,y ; src1
|
||||
sta $1000,y ; dst1
|
||||
lda $1000,y ; src2
|
||||
sta $1000,y ; dst2
|
||||
iny
|
||||
bne :-
|
||||
|
||||
sta RAMWRTOFF
|
||||
sta RAMRDOFF
|
||||
clc
|
||||
bit $02E4
|
||||
rts
|
||||
|
||||
poporg
|
||||
.endproc
|
||||
sizeof_zpproc := .sizeof(zpproc)
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
on_line_params_buffer := $220
|
||||
DEFINE_ON_LINE_PARAMS on_line_params, $30, on_line_params_buffer
|
||||
|
||||
num_banks_minus_one:
|
||||
.byte 0
|
||||
|
||||
L239F: .byte 0
|
||||
|
||||
sig: scrcode "GEB" ; signature sequence - Glen E. Bredon
|
||||
sig_len = * - sig
|
||||
|
||||
;; Volume Directory Header
|
||||
.proc vol_dir_header
|
||||
.word 0 ; preceding block number
|
||||
.word $03 ; succeeding block number
|
||||
.byte ST_VOLUME_DIRECTORY << 4 | 3 ; storage type / name length
|
||||
.byte "RAM" ; name field is 15 bytes
|
||||
.res 15-3
|
||||
.res 8, 0 ; reserved (8 bytes)
|
||||
.word 0, 0 ; creation date/time
|
||||
.byte 1 ; version (1 = ProDOS 2.0)
|
||||
.byte 0 ; min_version
|
||||
.byte ACCESS_DEFAULT ; access
|
||||
.byte $27 ; entry_length
|
||||
.byte $D ; entries_per_block
|
||||
.word 0 ; file_count
|
||||
.word 6 ; bit_map_pointer
|
||||
blocks: .word 0 ; total_blocks
|
||||
.endproc
|
||||
.assert .sizeof(vol_dir_header) = .sizeof(VolumeDirectoryHeader), error, "Size mismatch"
|
||||
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Ram Disk Driver - installed at $FF00
|
||||
;;; ============================================================
|
||||
|
||||
.proc driver_src
|
||||
pushorg ::driver_target
|
||||
driver_start := *
|
||||
|
||||
start: cld ; used as a signature
|
||||
|
||||
lda DRIVER_COMMAND
|
||||
bne not_status
|
||||
driver_blocks_lo := *+1
|
||||
ldx #0 ; self-modified - blocks low
|
||||
driver_blocks_hi := *+1
|
||||
ldy #0 ; self-modified - blocks high
|
||||
LFF09: clc
|
||||
bcc LFF83 ; always
|
||||
|
||||
not_status:
|
||||
cmp #DRIVER_COMMAND_FORMAT
|
||||
beq LFF09
|
||||
|
||||
;; COMMAND_READ or COMMAND_WRITE
|
||||
LFF10: lda #$27
|
||||
bcs rts1
|
||||
|
||||
lda RD80STORE
|
||||
pha
|
||||
sta CLR80COL
|
||||
|
||||
;; Save $40/41
|
||||
lda $40
|
||||
pha
|
||||
lda $41
|
||||
pha
|
||||
|
||||
lda DRIVER_BUFFER
|
||||
sta $40
|
||||
ldx DRIVER_BUFFER+1
|
||||
inx
|
||||
stx $41
|
||||
|
||||
jsr install_zpproc_relay
|
||||
|
||||
zpproc_relay_patch1_offset := $04
|
||||
stx zpproc_relay_addr + zpproc_relay_patch1_offset
|
||||
lda RDALTZP
|
||||
|
||||
zpproc_relay_patch2_offset := $14
|
||||
sta zpproc_relay_addr + zpproc_relay_patch2_offset
|
||||
lda DRIVER_BLOCK_NUMBER+1
|
||||
pha
|
||||
tax
|
||||
lda DRIVER_BLOCK_NUMBER
|
||||
LFF3C: sec
|
||||
: iny
|
||||
sbc #$7F
|
||||
bcs :-
|
||||
dex
|
||||
bpl LFF3C
|
||||
|
||||
tya
|
||||
adc DRIVER_BLOCK_NUMBER
|
||||
bcc :+
|
||||
inc DRIVER_BLOCK_NUMBER+1
|
||||
: asl a
|
||||
tay
|
||||
lda DRIVER_BLOCK_NUMBER+1
|
||||
rol a
|
||||
tax
|
||||
pla
|
||||
sta DRIVER_BLOCK_NUMBER+1
|
||||
driver_block_x := *+1
|
||||
cpx #$0 ; self-modified - ???
|
||||
bcs LFF74
|
||||
|
||||
tya
|
||||
sbc #191
|
||||
cmp #16
|
||||
bcs :+
|
||||
adc #208
|
||||
|
||||
tay
|
||||
bit LCBANK2
|
||||
: lda DRIVER_COMMAND
|
||||
lsr a ; carry set = READ, clear = WRITE
|
||||
lda bank_list,x
|
||||
ldx DRIVER_BUFFER
|
||||
jsr zpproc_relay_addr
|
||||
bit LCBANK1
|
||||
|
||||
LFF74: jsr install_zpproc_relay
|
||||
|
||||
;; Restore $40/41
|
||||
pla
|
||||
sta $41
|
||||
pla
|
||||
sta $40
|
||||
|
||||
pla
|
||||
bpl LFF83
|
||||
sta SET80COL
|
||||
LFF83: lda #$00
|
||||
bcs LFF10
|
||||
|
||||
rts1: rts
|
||||
|
||||
install_zpproc_relay:
|
||||
ldy #sizeof_zpproc_relay+1
|
||||
: ldx zpproc_relay-1,y
|
||||
lda zpproc_relay_addr-1,y
|
||||
sta zpproc_relay-1,y
|
||||
txa
|
||||
sta zpproc_relay_addr-1,y
|
||||
dey
|
||||
bne :-
|
||||
|
||||
ldx DRIVER_BUFFER+1
|
||||
bpl done
|
||||
bit DRIVER_BUFFER+1
|
||||
bvc done
|
||||
|
||||
: ldx $8000,y
|
||||
lda (DRIVER_BUFFER),y
|
||||
sta $8000,y
|
||||
txa
|
||||
sta (DRIVER_BUFFER),y
|
||||
ldx $8100,y
|
||||
lda ($40),y
|
||||
sta $8100,y
|
||||
txa
|
||||
sta ($40),y
|
||||
iny
|
||||
bne :-
|
||||
|
||||
ldx #$80
|
||||
done: rts
|
||||
|
||||
bank_list:
|
||||
.res ::kMaxUsableBanks, 0
|
||||
|
||||
.proc zpproc_relay
|
||||
sta BANKSEL
|
||||
|
||||
patch_loc1 := *+1
|
||||
lda #$00
|
||||
sta ALTZPON
|
||||
jsr zpproc_addr
|
||||
sty BANKSEL
|
||||
bmi :+
|
||||
sta ALTZPOFF
|
||||
: rts
|
||||
|
||||
patch_loc2 := *
|
||||
.endproc
|
||||
sizeof_zpproc_relay := .sizeof(zpproc_relay)
|
||||
patch_loc1_offset := zpproc_relay::patch_loc1 - zpproc_relay
|
||||
patch_loc2_offset := zpproc_relay::patch_loc2 - zpproc_relay
|
||||
;; These offsets can't be used directly due to ca65 addressing mode
|
||||
;; assumptions, so just verify they are correct.
|
||||
.assert zpproc_relay_patch1_offset = patch_loc1_offset, error, "Offset mismatch"
|
||||
.assert zpproc_relay_patch2_offset = patch_loc2_offset, error, "Offset mismatch"
|
||||
|
||||
.byte 0
|
||||
|
||||
poporg
|
||||
.endproc
|
||||
sizeof_driver := .sizeof(driver_src)
|
||||
|
||||
driver_blocks_lo := driver_src + driver_src::driver_blocks_lo - driver_src::driver_start
|
||||
driver_blocks_hi := driver_src + driver_src::driver_blocks_hi - driver_src::driver_start
|
||||
driver_block_x := driver_src + driver_src::driver_block_x - driver_src::driver_start
|
||||
driver_bank_list := driver_src + driver_src::bank_list - driver_src::driver_start
|
||||
|
||||
;;; ============================================================
|
||||
;;; Scratch space beyond code used during driver install
|
||||
|
||||
reserved_banks := *
|
||||
first_used_bank := *+1
|
||||
map1 := *+2 ; len: $80
|
||||
map2 := *+2+$80 ; len: $80
|
||||
stash_00 := *+2+$100 ; len: $80
|
||||
stash_01 := *+2+$180 ; len: $80
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
9
ram.drv/res/go.sh
Executable file
9
ram.drv/res/go.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Run this from the ram.drv.system directory
|
||||
|
||||
set -e
|
||||
source "res/util.sh"
|
||||
|
||||
#do_make clean
|
||||
do_make all
|
15
ram.drv/res/util.sh
Normal file
15
ram.drv/res/util.sh
Normal file
|
@ -0,0 +1,15 @@
|
|||
function cecho {
|
||||
case $1 in
|
||||
red) tput setaf 1 ; shift ;;
|
||||
green) tput setaf 2 ; shift ;;
|
||||
yellow) tput setaf 3 ; shift ;;
|
||||
esac
|
||||
echo -e "$@"
|
||||
tput sgr0
|
||||
}
|
||||
|
||||
function do_make {
|
||||
make $MAKE_FLAGS "$1" \
|
||||
&& (cecho green "make $1 good") \
|
||||
|| (tput blink ; cecho red "MAKE $1 BAD" ; return 1)
|
||||
}
|
45
selectors/Makefile
Normal file
45
selectors/Makefile
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/selector.system.SYS \
|
||||
$(OUTDIR)/selector.setup.SYS \
|
||||
$(OUTDIR)/bye.system.SYS \
|
||||
$(OUTDIR)/bye.setup.SYS \
|
||||
$(OUTDIR)/bbb.system.SYS \
|
||||
$(OUTDIR)/bbb.setup.SYS \
|
||||
$(OUTDIR)/buhbye.system.SYS \
|
||||
$(OUTDIR)/buhbye.setup.SYS
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
46
selectors/README.md
Normal file
46
selectors/README.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# ProDOS Program Selectors ("BYE" commands)
|
||||
|
||||
The ProDOS operating system for the Apple II personal computer line supported a quit routine (invoked from BASIC with the `BYE` command) allowing the user to launch a new system file once previous system file had exited.
|
||||
|
||||
This selector code evolved over time, and the memory location that the routine was stored at was documented, allowing users to install customized versions coded to fit within a mere 768 bytes.
|
||||
|
||||
## ProDOS 1.0 through 1.8 - Selector
|
||||
|
||||
The earliest versions of ProDOS supported a simple 40-column-friendly selector prompting:
|
||||
|
||||
* `ENTER PREFIX (PRESS "RETURN" TO ACCEPT)"`
|
||||
* `ENTER PATHNAME OF NEXT APPLICATION`
|
||||
|
||||
This was not particularly user friendly.
|
||||
|
||||
ℹ️ If you want to use this selector with any version of ProDOS, add the `SELECTOR.SYSTEM` file to your boot sequence. You can follow it with `QUIT.SYSTEM` in your driver sequence to show the selector on startup.
|
||||
|
||||
## Bird's Better Bye
|
||||
|
||||
[Alan Bird](https://alanlbird.wordpress.com/products/) wrote a replacement called **Bird's Better Bye** that would patch itself into ProDOS. Directories and system files could be selected with arrows keys and the Return key, Escape would back up a directory level, Tab would change drives. This also functioned in 40 column mode.
|
||||
|
||||
ℹ️ If you want to use this selector with any version of ProDOS, add the `BBB.SYSTEM` file to your boot sequence. You can follow it with `QUIT.SYSTEM` in your driver sequence to show the selector on startup.
|
||||
|
||||
## ProDOS 1.9 and 2.0.x - 80-column Selector
|
||||
|
||||
ProDOS 1.9 introduced a much improved menu-driven selector showing a list of files and allowing navigation of the file system with the keyboard. Directories and system files could be selected with arrows keys and the Return key, Escape would back up a directory level, Tab would change drives. This required 80 columns and a 65C02 processor, and took advantage of the [MouseText characters](https://en.wikipedia.org/wiki/MouseText) to show folder glyphs for directories.
|
||||
|
||||
If these versions of ProDOS were started on systems without 40 column support, the previous version of the selector would be loaded instead (both were present in the PRODOS system file.)
|
||||
|
||||
ℹ️ If you want to use this selector with any version of ProDOS, add the `BYE.SYSTEM` file to your boot sequence. You can follow it with `QUIT.SYSTEM` in your driver sequence to show the selector on startup.
|
||||
|
||||
This was inspired by the work of Dave Cotter who created a similarly named file to patch the selector back in. It can be found at: http://www.lazilong.com/apple_ii/bye.sys/bye.html
|
||||
|
||||
## ProDOS 2.4 - Bitsy Bye
|
||||
|
||||
The new (unofficial) releases of [ProDOS 2.4](http://www.callapple.org/uncategorized/announcing-prodos-2-4-for-all-apple-ii-computers/) by John Brooks include a replacement quit routine called Bitsy Bye, a collaboration with Peter Ferrie. This new quit routine is far more powerful, allowing access to BASIC and binary files (and more), drive selection, type-down, more entries, and so on. It uses only 40 columns, and does not require a 65C02 processor.
|
||||
|
||||
ℹ️ If you want to use this selector, use a version of ProDOS 2.4 from https://prodos8.com/
|
||||
|
||||
## Buh-Bye - Enhanced 80-column selector
|
||||
|
||||
Since I prefered the look of the ProDOS 80-column selector to Bitsy Bye, but missed the type-down ability, I modified the 80-column selector to tighten up the code added seeking if an alphabetical key is typed. Hit 'C' and the list will scroll to the next file starting with 'C'.
|
||||
|
||||
I erroneously thought that the ProDOS 80-column selector was _Bird's Better Bye_ and named this "Bell's Better Bird's Better Bye" or "Buh-Bye".
|
||||
|
||||
ℹ️ If you want to use this selector with any version of ProDOS, add the `BUHBYE.SYSTEM` file to your boot sequence. You can follow it with `QUIT.SYSTEM` in your driver sequence to show the selector on startup.
|
592
selectors/bbb.system.s
Normal file
592
selectors/bbb.system.s
Normal file
|
@ -0,0 +1,592 @@
|
|||
;;; Disassembly of Bird's Better Bye, 40 column program selector
|
||||
;;; (Found in a copy of ProDOS 1.8, but not believed to be original?)
|
||||
;;;
|
||||
;;; Installer wrapper added by Joshua Bell inexorabletash@gmail.com
|
||||
|
||||
.setcpu "65C02"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
.include "../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; ProDOS Technical Reference Manual, 5.1.5.2:
|
||||
;;;
|
||||
;;; ProDOS MLI call $65, the QUIT call, moves addresses $D100 through
|
||||
;;; $D3FF from the second 4K bank of RAM of the language card to
|
||||
;;; $1000, and executes a JMP to $1000. What initially resides in that
|
||||
;;; area is Apple's dispatcher code.
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Installer
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
max_size = $300
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
src := install_src
|
||||
end := install_src + install_size
|
||||
dst := $D100 ; Install location in ProDOS (bank 2)
|
||||
|
||||
src_ptr := $19
|
||||
dst_ptr := $1B
|
||||
|
||||
sta ALTZPOFF
|
||||
lda ROMIN
|
||||
lda ROMIN
|
||||
lda #>src
|
||||
sta src_ptr+1
|
||||
lda #<src
|
||||
sta src_ptr
|
||||
lda #>dst
|
||||
sta dst_ptr+1
|
||||
lda #<dst
|
||||
sta dst_ptr
|
||||
|
||||
loop: lda (src_ptr)
|
||||
sta (dst_ptr)
|
||||
inc src_ptr
|
||||
bne :+
|
||||
inc src_ptr+1
|
||||
: inc dst_ptr
|
||||
bne :+
|
||||
inc dst_ptr+1
|
||||
: lda src_ptr+1
|
||||
cmp #>end
|
||||
bne loop
|
||||
lda src_ptr
|
||||
cmp #<end
|
||||
bne loop
|
||||
lda (src_ptr)
|
||||
sta (dst_ptr)
|
||||
sta ALTZPOFF
|
||||
sta ROMINWB1
|
||||
sta ROMINWB1
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Selector
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
install_src := *
|
||||
|
||||
pushorg $1000
|
||||
.proc bbb
|
||||
|
||||
|
||||
PREFIX := $280
|
||||
|
||||
kMaxFilesDisplayed = 16
|
||||
|
||||
filename_table := $1700
|
||||
device := $6 ; current device index (in DEVLST)
|
||||
name_len := $8
|
||||
index := $9 ; current displayed file index
|
||||
name_ptr := $D
|
||||
num_files := $F ; number of displayed files
|
||||
type_table := $10 ; one entry per line, high bit set if SYS
|
||||
|
||||
;; Copied from directory header
|
||||
entry_length := $A5
|
||||
entries_per_block := $A6
|
||||
file_count := $A7
|
||||
|
||||
entry_index := $A9 ; within "block"
|
||||
|
||||
dir_buf := SYS_ADDR
|
||||
|
||||
;;; ============================================================
|
||||
;;; Code
|
||||
;;; ============================================================
|
||||
|
||||
;; Signal to ProDOS that this is modified.
|
||||
cld
|
||||
|
||||
inc $03F4 ; ???
|
||||
|
||||
;; Page in normal banks, reset screen to 40 columns.
|
||||
lda ROMIN2
|
||||
jsr SETTXT
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
jsr SETNORM
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
sta CLR80COL
|
||||
|
||||
;; Clear system bitmap.
|
||||
lda #$01
|
||||
sta BITMAP+BITMAP_SIZE-1
|
||||
lda #$00
|
||||
ldx #BITMAP_SIZE-2
|
||||
: sta BITMAP,x
|
||||
dex
|
||||
bpl :-
|
||||
lda #$CF
|
||||
sta BITMAP
|
||||
|
||||
|
||||
lda default_devnum
|
||||
beq :+ ; always?
|
||||
sta DEVNUM
|
||||
: ldx DEVCNT
|
||||
: stx device
|
||||
lda DEVLST,x
|
||||
and #$F0
|
||||
cmp DEVNUM
|
||||
beq set_devnum
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
next_drive:
|
||||
ldx device
|
||||
cpx DEVCNT
|
||||
bcc :+
|
||||
ldx #$FF
|
||||
: inx
|
||||
stx device
|
||||
|
||||
lda DEVLST,x
|
||||
set_devnum:
|
||||
sta on_line_unit_num
|
||||
sta DEVNUM
|
||||
jsr HOME
|
||||
MLI_CALL ON_LINE, on_line_params
|
||||
bcs error_relay
|
||||
lda PREFIX+1
|
||||
and #$0F
|
||||
beq error_relay
|
||||
adc #$02
|
||||
sta PREFIX
|
||||
tax
|
||||
set_prefix_length:
|
||||
lda #'/'
|
||||
sta PREFIX+1
|
||||
sta PREFIX,x
|
||||
lda #0
|
||||
sta PREFIX+1,x
|
||||
sta index
|
||||
sta read_request_count+1
|
||||
sta set_mark_position+1
|
||||
sta set_mark_position+2
|
||||
|
||||
ldx #msg_select_drive
|
||||
lda #10 ; HTAB
|
||||
jsr ShowMessage
|
||||
|
||||
ldx #msg_select_file
|
||||
lda #9 ; HTAB
|
||||
jsr ShowMessage
|
||||
|
||||
;; Show current prefix
|
||||
ldx #0
|
||||
: lda PREFIX+1,x
|
||||
beq :+
|
||||
jsr Cout
|
||||
inx
|
||||
bne :-
|
||||
|
||||
: MLI_CALL OPEN, open_params
|
||||
error_relay:
|
||||
bcs next_drive_on_error
|
||||
jsr AssignRefNum
|
||||
lda #.sizeof(SubdirectoryHeader)
|
||||
sta read_request_count
|
||||
MLI_CALL READ, read_params
|
||||
bcs next_drive_on_error
|
||||
|
||||
ldx #3
|
||||
: lda dir_buf + SubdirectoryHeader::entry_length,x
|
||||
sta entry_length,x
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
sta read_request_count
|
||||
lda #1
|
||||
sta entry_index
|
||||
lda file_count ; empty?
|
||||
ora file_count+1
|
||||
bne next_file
|
||||
: jmp finish_dir ; empty!
|
||||
|
||||
;; Loop over file entries
|
||||
next_file:
|
||||
bit file_count+1 ; negative?
|
||||
bmi :-
|
||||
skip: lda set_mark_position+1
|
||||
and #$FE
|
||||
sta set_mark_position+1
|
||||
ldy entry_index
|
||||
lda #0
|
||||
cpy entries_per_block
|
||||
bcc next_entry_in_block
|
||||
tay
|
||||
sty entry_index
|
||||
inc set_mark_position+1 ; next block - two pages
|
||||
: inc set_mark_position+1
|
||||
next_entry_in_block:
|
||||
dey
|
||||
clc
|
||||
bmi next_block
|
||||
adc entry_length
|
||||
bcc next_entry_in_block
|
||||
bcs :- ; always
|
||||
|
||||
next_block:
|
||||
adc #4 ; skip prev/next block pointers
|
||||
sta set_mark_position
|
||||
MLI_CALL SET_MARK, get_eof_params
|
||||
|
||||
next_drive_on_error:
|
||||
bcs do_next_drive
|
||||
|
||||
MLI_CALL READ, read_params
|
||||
bcs do_next_drive
|
||||
inc entry_index
|
||||
lda dir_buf
|
||||
beq skip ; deleted entry
|
||||
and #NAME_LENGTH_MASK
|
||||
sta dir_buf
|
||||
dec file_count
|
||||
bne :+
|
||||
dec file_count+1
|
||||
: ror dir_buf + FileEntry::access ; check low bit
|
||||
bcc next_file
|
||||
|
||||
lda dir_buf + FileEntry::file_type
|
||||
cmp #FT_DIRECTORY
|
||||
beq :+
|
||||
cmp #FT_SYSTEM
|
||||
bne next_file
|
||||
: ldx index
|
||||
cpx #kMaxFilesDisplayed
|
||||
bcs finish_dir
|
||||
sta type_table,x
|
||||
jsr SetNamePtrAndLen
|
||||
|
||||
ldy #$0F ; max name length
|
||||
: lda dir_buf,y
|
||||
sta (name_ptr),y
|
||||
dey
|
||||
bpl :-
|
||||
|
||||
jsr DisplayFilename
|
||||
inc index
|
||||
jmp next_file
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
do_next_drive:
|
||||
jmp next_drive
|
||||
|
||||
up_dir:
|
||||
ldx PREFIX
|
||||
: dex
|
||||
beq input_loop
|
||||
lda PREFIX,x
|
||||
cmp #'/'
|
||||
bne :-
|
||||
dex
|
||||
beq input_loop
|
||||
stx PREFIX
|
||||
|
||||
set_new_prefix:
|
||||
inc PREFIX
|
||||
jsr HOME
|
||||
ldx PREFIX
|
||||
jmp set_prefix_length
|
||||
|
||||
select_next_file:
|
||||
jsr DisplayFilename
|
||||
ldx index
|
||||
inx
|
||||
cpx num_files
|
||||
bcc set_index
|
||||
ldx #0
|
||||
beq set_index ; always
|
||||
|
||||
select_prev_file:
|
||||
jsr DisplayFilename
|
||||
ldx index
|
||||
bne :+
|
||||
ldx num_files
|
||||
: dex
|
||||
set_index:
|
||||
stx index
|
||||
jmp redisplay_selection
|
||||
|
||||
finish_dir:
|
||||
MLI_CALL CLOSE, close_params
|
||||
next_drive_relay:
|
||||
bcs do_next_drive
|
||||
lda index
|
||||
beq do_next_drive
|
||||
sta num_files
|
||||
lda #0
|
||||
sta index
|
||||
redisplay_selection:
|
||||
jsr SETINV
|
||||
jsr DisplayFilename
|
||||
jsr SETNORM
|
||||
;; fall through
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
input_loop:
|
||||
lda KBD
|
||||
bpl input_loop
|
||||
sta KBDSTRB
|
||||
|
||||
cmp #HI(ASCII_TAB)
|
||||
beq do_next_drive
|
||||
|
||||
cmp #HI(ASCII_LEFT)
|
||||
beq select_prev_file
|
||||
cmp #HI(ASCII_UP)
|
||||
beq select_prev_file
|
||||
|
||||
cmp #HI(ASCII_RIGHT)
|
||||
beq select_next_file
|
||||
cmp #HI(ASCII_DOWN)
|
||||
beq select_next_file
|
||||
|
||||
cmp #HI(ASCII_ESCAPE)
|
||||
beq up_dir
|
||||
|
||||
cmp #HI(ASCII_CR)
|
||||
bne input_loop
|
||||
lda BUTN0
|
||||
bpl SelectFile
|
||||
|
||||
lda ROMIN
|
||||
lda ROMIN
|
||||
lda DEVNUM
|
||||
sta $D3A6
|
||||
lda ROMIN2
|
||||
jmp input_loop
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc SelectFile
|
||||
MLI_CALL SET_PREFIX, set_prefix_params
|
||||
bcs next_drive_relay
|
||||
jsr SetNamePtrAndLen
|
||||
ldx PREFIX
|
||||
: iny
|
||||
lda (name_ptr),y
|
||||
inx
|
||||
sta PREFIX,x
|
||||
cpy name_len
|
||||
bcc :-
|
||||
stx PREFIX
|
||||
ldy index
|
||||
lda type_table,y
|
||||
bmi InvokeSystemFile
|
||||
jmp set_new_prefix
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc InvokeSystemFile
|
||||
MLI_CALL OPEN, open_params
|
||||
: bcs next_drive_relay
|
||||
jsr AssignRefNum
|
||||
MLI_CALL GET_EOF, get_eof_params
|
||||
bcs :-
|
||||
lda get_eof_eof
|
||||
sta read_request_count
|
||||
lda get_eof_eof+1
|
||||
sta read_request_count+1
|
||||
MLI_CALL READ, read_params
|
||||
php
|
||||
MLI_CALL CLOSE, close_params
|
||||
bcc :+
|
||||
pla
|
||||
err: jmp do_next_drive
|
||||
|
||||
: plp
|
||||
bcs err
|
||||
jmp SYS_ADDR
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Call with HTAB in A, message table offset in X
|
||||
|
||||
.proc ShowMessage
|
||||
sta CH
|
||||
|
||||
: lda message_table,x
|
||||
beq done
|
||||
jsr Cout
|
||||
inx
|
||||
bne :-
|
||||
done: rts
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc Cout
|
||||
ora #$80
|
||||
jmp COUT1
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc SetNamePtrAndLen
|
||||
lda index
|
||||
asl a ; * 16 (name length)
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sta name_ptr
|
||||
lda #>filename_table
|
||||
sta name_ptr+1
|
||||
ldy #$00
|
||||
lda (name_ptr),y
|
||||
sta name_len
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc DisplayFilename
|
||||
lda #5
|
||||
sta CH
|
||||
lda index
|
||||
clc
|
||||
adc #$05
|
||||
jsr TABV
|
||||
jsr space
|
||||
ldx index
|
||||
lda type_table,x ; folder?
|
||||
bmi :+ ; nope
|
||||
lda #HI('/') ; yes - suffix with '/'
|
||||
jsr Cout
|
||||
: jsr SetNamePtrAndLen
|
||||
|
||||
: iny
|
||||
lda (name_ptr),y
|
||||
jsr Cout
|
||||
cpy name_len
|
||||
bcc :-
|
||||
|
||||
space: lda #HI(' ')
|
||||
jmp Cout
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc AssignRefNum
|
||||
lda open_ref_num
|
||||
sta read_ref_num
|
||||
sta get_eof_ref_num ; also set_mark_position
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
default_devnum:
|
||||
.byte 0
|
||||
|
||||
;;; ============================================================
|
||||
;;; Messages
|
||||
;;; ============================================================
|
||||
|
||||
message_table:
|
||||
|
||||
msg_select_drive := * - message_table
|
||||
scrcode "TAB: SELECT DRIVE\r"
|
||||
.byte 0
|
||||
|
||||
msg_select_file := * - message_table
|
||||
scrcode "RETURN: SELECT FILE\r\r"
|
||||
.byte 0
|
||||
|
||||
;;; ============================================================
|
||||
;;; Parameter Blocks
|
||||
;;; ============================================================
|
||||
|
||||
;;; OPEN params
|
||||
|
||||
open_params:
|
||||
.byte 3
|
||||
open_pathname:
|
||||
.addr PREFIX
|
||||
open_io_buffer:
|
||||
.addr $1800
|
||||
open_ref_num:
|
||||
.byte 0
|
||||
|
||||
;;; CLOSE params
|
||||
|
||||
close_params:
|
||||
.byte 1
|
||||
.byte 0
|
||||
|
||||
;;; ON_LINE params
|
||||
|
||||
on_line_params:
|
||||
.byte $02
|
||||
on_line_unit_num:
|
||||
.byte $60
|
||||
on_line_buffer:
|
||||
.addr PREFIX+1
|
||||
|
||||
;;; READ params
|
||||
|
||||
read_params:
|
||||
.byte 4
|
||||
read_ref_num:
|
||||
.byte 0
|
||||
read_data_buffer:
|
||||
.addr $2000
|
||||
read_request_count:
|
||||
.word 0
|
||||
read_transfer_count:
|
||||
.word 0
|
||||
|
||||
;;; SET_PREFIX params
|
||||
|
||||
set_prefix_params:
|
||||
.byte 1
|
||||
.addr PREFIX
|
||||
|
||||
;;; GET_EOF params
|
||||
;;; SET_MARK params
|
||||
|
||||
get_eof_params:
|
||||
set_mark_params:
|
||||
.byte 2
|
||||
get_eof_ref_num:
|
||||
set_mark_ref_num:
|
||||
.byte 0
|
||||
get_eof_eof:
|
||||
set_mark_position:
|
||||
.faraddr $A0A0A0
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.endproc
|
||||
.assert .sizeof(bbb) - bbb <= $300, error, "Must fit in $300 bytes"
|
||||
install_size = $300
|
||||
poporg
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
701
selectors/buhbye.system.s
Normal file
701
selectors/buhbye.system.s
Normal file
|
@ -0,0 +1,701 @@
|
|||
;;; Enhancements to the 80-column ProDOS 8 Selector ("BYE") found in
|
||||
;;; ProDOS 1.9 and 2.0.x.
|
||||
;;;
|
||||
;;; Modifications by Joshua Bell inexorabletash@gmail.com
|
||||
;;; * alpha key advances to next matching filename
|
||||
;;; * replaced directory enumeration (smaller, per PDTRM)
|
||||
;;; * installs, then chains to next .SYSTEM file
|
||||
|
||||
.setcpu "65C02"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
.include "../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; ProDOS Technical Reference Manual, 5.1.5.2:
|
||||
;;;
|
||||
;;; ProDOS MLI call $65, the QUIT call, moves addresses $D100 through
|
||||
;;; $D3FF from the second 4K bank of RAM of the language card to
|
||||
;;; $1000, and executes a JMP to $1000. What initially resides in that
|
||||
;;; area is Apple's dispatcher code.
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Installer
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
max_size = $300
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
src := install_src
|
||||
end := install_src + install_size
|
||||
dst := $D100 ; Install location in ProDOS (bank 2)
|
||||
|
||||
src_ptr := $19
|
||||
dst_ptr := $1B
|
||||
|
||||
sta ALTZPOFF ; TODO: Necessary?
|
||||
lda ROMIN ; write bank 2
|
||||
lda ROMIN
|
||||
|
||||
lda #<src ; src_ptr = src
|
||||
sta src_ptr
|
||||
lda #>src
|
||||
sta src_ptr+1
|
||||
|
||||
lda #<dst ; dst_ptr = dst
|
||||
sta dst_ptr
|
||||
lda #>dst
|
||||
sta dst_ptr+1
|
||||
|
||||
loop: lda (src_ptr) ; *src_ptr = *dst_ptr
|
||||
sta (dst_ptr)
|
||||
|
||||
inc src_ptr ; src_ptr++
|
||||
bne :+
|
||||
inc src_ptr+1
|
||||
|
||||
: inc dst_ptr ; dst_ptr++
|
||||
bne :+
|
||||
inc dst_ptr+1
|
||||
|
||||
: lda src_ptr+1 ; src_ptr == end ?
|
||||
cmp #>end
|
||||
bne loop
|
||||
lda src_ptr
|
||||
cmp #<end
|
||||
bne loop
|
||||
|
||||
sta ALTZPOFF ; TODO: Necessary?
|
||||
sta ROMINWB1
|
||||
sta ROMINWB1
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Selector
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
install_src := *
|
||||
|
||||
pushorg $1000
|
||||
.proc selector
|
||||
|
||||
prefix := $280 ; length-prefixed
|
||||
|
||||
filenames := $1400 ; each is length + 15 bytes
|
||||
read_buffer := $2000 ; Also, start location for launched SYS files
|
||||
|
||||
;; Device/Prefix enumeration
|
||||
cur_device_num := $65 ; device number to try
|
||||
prefix_depth := $6B ; 0 = root
|
||||
|
||||
;; Directory enumeration
|
||||
entry_pointer := $60 ; 2 bytes
|
||||
block_entries := $62
|
||||
active_entries := $63 ; 2 bytes
|
||||
|
||||
entry_length := $6E
|
||||
entries_per_block := $6F
|
||||
file_count := $70 ; 2 bytes
|
||||
|
||||
;; Found entries
|
||||
current_entry := $67 ; index of current entry
|
||||
num_entries := $68 ; length of |filenames| (max 128)
|
||||
curr_len := $69 ; length of current entry name
|
||||
curr_ptr := $6C ; address of current entry name (in |filenames|)
|
||||
types_table := $74 ; high bit clear = dir, set = sys
|
||||
|
||||
;; Entry display
|
||||
page_start := $73 ; index of first entry shown on screen
|
||||
row_count := $6A ; number of rows in this page
|
||||
top_row := 2 ; first row used on screen
|
||||
bottom_row := 21 ; last row used on screen
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
cld ; ProDOS protocol for QUIT routine
|
||||
lda ROMIN2 ; Page in ROM for reads, writes ignored
|
||||
|
||||
bit KBDSTRB ; Clear keyboard strobe
|
||||
|
||||
lda #$A0
|
||||
jsr SLOT3 ; Activate 80-Column Firmware
|
||||
|
||||
;; Update system bitmap
|
||||
ldx #BITMAP_SIZE-1 ; zero it all out
|
||||
: stz BITMAP,x
|
||||
dex
|
||||
bpl :-
|
||||
inc BITMAP+BITMAP_SIZE-1 ; protect ProDOS global page
|
||||
lda #%11001111 ; protect zp, stack, text page 1
|
||||
sta BITMAP
|
||||
|
||||
;; Find device
|
||||
ldx DEVCNT
|
||||
: lda DEVLST,x
|
||||
and #%11110000
|
||||
cmp DEVNUM
|
||||
beq check_device
|
||||
dex
|
||||
bra :-
|
||||
|
||||
next_device:
|
||||
ldx cur_device_num
|
||||
dex
|
||||
bpl :+
|
||||
ldx DEVCNT
|
||||
: lda DEVLST,x
|
||||
|
||||
check_device:
|
||||
stx cur_device_num
|
||||
sta on_line_params_unit
|
||||
MLI_CALL ON_LINE, on_line_params
|
||||
bcs next_device
|
||||
|
||||
stz prefix_depth
|
||||
lda prefix+1
|
||||
and #$0F
|
||||
beq next_device
|
||||
adc #2
|
||||
tax
|
||||
|
||||
;; Resize prefix to length x and open the directory for reading
|
||||
.proc resize_prefix_and_open
|
||||
stx prefix
|
||||
lda #'/'
|
||||
sta prefix+1 ; ensure prefix is at least '/'
|
||||
sta prefix,x ; and ends with '/'
|
||||
stz prefix+1,x ; and is null terminated
|
||||
|
||||
stz num_entries
|
||||
|
||||
;;; Enumerate directory
|
||||
;;; Algorithm from ProDOS Technical Reference Manual B.2.5
|
||||
|
||||
;; Open the directory
|
||||
jsr do_open
|
||||
bcc :+
|
||||
|
||||
;; Open failed
|
||||
fail: lda prefix_depth ; root?
|
||||
beq next_device
|
||||
jsr pop_prefix ; and go up a level
|
||||
bra resize_prefix_and_open
|
||||
|
||||
;; Open succeeded
|
||||
: inc prefix_depth
|
||||
|
||||
;; Read a block (512 bytes) into buffer
|
||||
stz read_params_request
|
||||
lda #2
|
||||
sta read_params_request+1
|
||||
jsr do_read
|
||||
bcs fail
|
||||
|
||||
;; Store entry_length (byte), entries_per_block (byte), file_count (word)
|
||||
ldx #3
|
||||
: lda read_buffer + SubdirectoryHeader::entry_length,x
|
||||
sta entry_length,x
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
;; Any entries?
|
||||
lda file_count
|
||||
ora file_count+1
|
||||
beq close_dir
|
||||
|
||||
;; Skip header entry
|
||||
clc
|
||||
lda #<(read_buffer+4) ; 4 bytes for prev/next pointers
|
||||
adc entry_length
|
||||
sta entry_pointer
|
||||
lda #>(read_buffer+4)
|
||||
adc #0 ; TODO: Can skip this if entry_length << 256
|
||||
sta entry_pointer+1
|
||||
|
||||
;; Prepare to process entry two (first "entry" is header)
|
||||
lda #2
|
||||
sta block_entries
|
||||
|
||||
while_loop:
|
||||
;; Check if entry is active
|
||||
lda (entry_pointer)
|
||||
beq done_entry
|
||||
|
||||
;; Check file type
|
||||
ldy #FileEntry::file_type
|
||||
lda (entry_pointer),y
|
||||
cmp #FT_DIRECTORY
|
||||
beq store_entry
|
||||
cmp #FT_SYSTEM
|
||||
bne done_active_entry
|
||||
|
||||
store_entry:
|
||||
;; Store type
|
||||
ldx num_entries
|
||||
sta types_table,x
|
||||
|
||||
;; Copy name into |filenames|
|
||||
jsr update_curr_ptr ; current entry in X
|
||||
ldy #15 ; max name length (length byte copied too)
|
||||
: lda (entry_pointer),y
|
||||
sta (curr_ptr),y
|
||||
dey
|
||||
bpl :-
|
||||
; storage_type/name_length in A
|
||||
and #%00001111 ; mask off name_length (remove storage_type)
|
||||
sta (curr_ptr) ; store length
|
||||
|
||||
inc num_entries
|
||||
|
||||
done_active_entry:
|
||||
dec file_count
|
||||
bpl :+
|
||||
dec file_count+1
|
||||
:
|
||||
|
||||
done_entry:
|
||||
;; Seen all active entries?
|
||||
lda file_count
|
||||
ora file_count+1
|
||||
beq close_dir
|
||||
|
||||
;; Seen all entries in this block?
|
||||
lda block_entries
|
||||
cmp entries_per_block
|
||||
bne next_in_block
|
||||
|
||||
;; Grab next block
|
||||
next_block:
|
||||
jsr do_read ; read another block
|
||||
bcs fail
|
||||
|
||||
lda #1 ; first entry in non-key block
|
||||
sta block_entries
|
||||
|
||||
lda #<(read_buffer+4) ; 4 bytes for prev/next pointers
|
||||
sta entry_pointer
|
||||
lda #>(read_buffer+4)
|
||||
sta entry_pointer+1
|
||||
|
||||
bra end_while
|
||||
|
||||
;; Next entry in current block
|
||||
next_in_block:
|
||||
clc
|
||||
lda entry_pointer
|
||||
adc entry_length
|
||||
sta entry_pointer
|
||||
bcc :+
|
||||
inc entry_pointer+1
|
||||
:
|
||||
|
||||
inc block_entries
|
||||
|
||||
end_while:
|
||||
;; Check to see if we have room
|
||||
bit num_entries ; max is 128
|
||||
bpl while_loop
|
||||
|
||||
close_dir:
|
||||
MLI_CALL CLOSE, close_params
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc draw_screen
|
||||
jsr SETTXT ; TEXT
|
||||
jsr HOME ; HOME
|
||||
lda #23 ; VTAB 23
|
||||
jsr TABV
|
||||
|
||||
;; Print help text
|
||||
lda #20 ; HTAB 20
|
||||
sta COL80HPOS
|
||||
ldy #(help_string - text_resources)
|
||||
jsr cout_string
|
||||
|
||||
;; Draw prefix
|
||||
jsr home
|
||||
ldx #0
|
||||
: lda prefix+1,x
|
||||
beq :+
|
||||
jsr ascii_cout
|
||||
inx
|
||||
bne :-
|
||||
|
||||
: stz current_entry
|
||||
stz page_start
|
||||
lda num_entries
|
||||
beq selection_loop_keyboard_loop ; no entries (empty directory)
|
||||
|
||||
;; Draw entries
|
||||
cmp #bottom_row ; more entries than fit?
|
||||
bcc :+
|
||||
lda #(bottom_row - top_row + 1)
|
||||
: sta row_count
|
||||
lda #top_row
|
||||
sta WNDTOP
|
||||
sta WNDLFT
|
||||
lda #bottom_row+1
|
||||
sta WNDWDTH
|
||||
sta WNDBTM
|
||||
loop: jsr draw_current_line
|
||||
inc current_entry
|
||||
dec row_count
|
||||
bne loop
|
||||
stz current_entry
|
||||
beq selection_loop
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; Remove level from prefix; returns new length in X
|
||||
.proc pop_prefix
|
||||
ldx prefix
|
||||
loop: dex
|
||||
lda prefix,x
|
||||
cmp #'/'
|
||||
bne loop
|
||||
cpx #1
|
||||
bne done
|
||||
ldx prefix
|
||||
done:
|
||||
;; Fall through...
|
||||
.endproc
|
||||
|
||||
handy_rts:
|
||||
rts
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_down
|
||||
jsr down_common
|
||||
bra selection_loop
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_up
|
||||
ldx current_entry ; first one?
|
||||
beq selection_loop
|
||||
dec current_entry ; go to previous
|
||||
|
||||
lda CV
|
||||
cmp #top_row ; at the top?
|
||||
bne selection_loop
|
||||
dec page_start ; yes, adjust page and
|
||||
lda #ASCII_SYN ; scroll screen up
|
||||
jsr COUT
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc selection_loop
|
||||
jsr SETINV
|
||||
jsr draw_current_line
|
||||
|
||||
keyboard_loop:
|
||||
lda KBD
|
||||
bpl keyboard_loop
|
||||
sta KBDSTRB
|
||||
jsr SETNORM
|
||||
|
||||
cmp #HI(ASCII_TAB)
|
||||
beq next_drive
|
||||
cmp #HI(ASCII_ESCAPE)
|
||||
beq on_escape
|
||||
|
||||
ldx num_entries
|
||||
beq keyboard_loop ; if empty, no navigation
|
||||
|
||||
pha
|
||||
jsr draw_current_line
|
||||
pla
|
||||
|
||||
cmp #HI(ASCII_CR)
|
||||
beq on_return
|
||||
cmp #HI(ASCII_DOWN)
|
||||
beq on_down
|
||||
cmp #HI(ASCII_UP)
|
||||
beq on_up
|
||||
;; fall through
|
||||
.endproc
|
||||
selection_loop_keyboard_loop := selection_loop::keyboard_loop
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_alpha
|
||||
loop: jsr down_common
|
||||
jsr draw_current_line
|
||||
lda KBD
|
||||
and #$5F ; make ASCII and uppercase
|
||||
ldy #1
|
||||
cmp (curr_ptr),y ; key = first char ?
|
||||
beq selection_loop
|
||||
bra loop
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_escape
|
||||
jsr pop_prefix ; leaves length in X
|
||||
dec prefix_depth
|
||||
bra resize_prefix_and_open_jmp
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
.proc down_common
|
||||
lda current_entry
|
||||
inc a
|
||||
cmp num_entries ; past the limit?
|
||||
bcc :+
|
||||
pla ; yes - abort subroutine
|
||||
pla
|
||||
bra selection_loop
|
||||
|
||||
: sta current_entry ; go to next
|
||||
|
||||
lda CV
|
||||
cmp #bottom_row ; at the bottom?
|
||||
bne handy_rts
|
||||
inc page_start ; yes, adjust page and
|
||||
lda #ASCII_ETB ; scroll screen down
|
||||
jmp COUT ; implicit rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
next_drive: ; relay for branches
|
||||
jmp next_device
|
||||
|
||||
inc_resize_prefix_and_open:
|
||||
inx
|
||||
|
||||
resize_prefix_and_open_jmp:
|
||||
jmp resize_prefix_and_open
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_return
|
||||
MLI_CALL SET_PREFIX, set_prefix_params
|
||||
bcs next_drive
|
||||
ldx current_entry
|
||||
jsr update_curr_ptr
|
||||
|
||||
ldx prefix
|
||||
: iny
|
||||
lda (curr_ptr),y
|
||||
inx
|
||||
sta prefix,x
|
||||
cpy curr_len
|
||||
bcc :-
|
||||
stx prefix
|
||||
|
||||
ldy current_entry
|
||||
lda types_table,y
|
||||
bpl inc_resize_prefix_and_open ; is directory???
|
||||
;; nope, system file, so...
|
||||
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc launch_sys_file
|
||||
jsr SETTXT
|
||||
jsr HOME
|
||||
lda #HI(ASCII_RIGHT) ; Right arrow ???
|
||||
jsr COUT
|
||||
|
||||
jsr do_open
|
||||
bcs next_drive
|
||||
lda #$FF ; Load up to $FFFF bytes
|
||||
sta read_params_request
|
||||
sta read_params_request+1
|
||||
jsr do_read
|
||||
php
|
||||
MLI_CALL CLOSE, close_params
|
||||
plp
|
||||
bcs next_drive
|
||||
jmp read_buffer ; Invoke the loaded code
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc cout_string
|
||||
loop: lda help_string,y
|
||||
beq handy_rts2
|
||||
jsr COUT
|
||||
iny
|
||||
bra loop
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; Compute address/length of curr_ptr/curr_len
|
||||
;; Call with entry index in X. Returns with Y = 0
|
||||
|
||||
.proc update_curr_ptr
|
||||
stz curr_ptr+1
|
||||
txa
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
sta curr_ptr
|
||||
lda #>filenames
|
||||
clc
|
||||
adc curr_ptr+1
|
||||
sta curr_ptr+1
|
||||
lda (curr_ptr)
|
||||
sta curr_len
|
||||
ldy #0 ; needed by caller
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
handy_rts2:
|
||||
rts
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc draw_current_line
|
||||
lda #2 ; hpos = 2
|
||||
sta COL80HPOS
|
||||
|
||||
ldx current_entry ; vpos = entry - page_start + 2
|
||||
txa
|
||||
sec
|
||||
sbc page_start
|
||||
inc a
|
||||
inc a
|
||||
jsr TABV
|
||||
|
||||
lda types_table,x
|
||||
bmi name ; is sys file?
|
||||
|
||||
;; Draw folder glyph
|
||||
stz COL80HPOS
|
||||
lda INVFLG
|
||||
pha
|
||||
ldy #(folder_string - text_resources) ; Draw folder glyphs
|
||||
jsr cout_string
|
||||
pla
|
||||
sta INVFLG
|
||||
|
||||
;; Work around MouseText deactivation bug on Franklin ACE 2X00/500
|
||||
;; The Apple IIe/IIc/IIgs and the Franklin ACE 2X00/500 all use the
|
||||
;; screen hole address $4FB to store a video firmware operating
|
||||
;; mode byte, e.g. whether or not MouseText output is active. For
|
||||
;; the Apple IIe and IIgs, see the Apple IIe Technical Reference
|
||||
;; Manual, Appendix J Monitor Firmware Listing for the definition.
|
||||
;; (Bit 0 is set when MouseText is inactive and clear when active.)
|
||||
;; The Apple IIc also uses bit 0 the same way, but bits 3 and 7 are
|
||||
;; also normally set. The Franklin Ace 2X00 and 500 set bit 6
|
||||
;; when MouseText mode is activated by printing $1B, but fails to
|
||||
;; clear it when printing $18, so MouseText remains active. This
|
||||
;; seems to be a bug in the firmware. Work around it by detecting
|
||||
;; the unique byte signature left behind and resetting it.
|
||||
lda $4FB ; $4FB = $30 normally (V flag clear)
|
||||
cmp #$40 ; $4FB = $70 if MT is active (V flag set)
|
||||
bne :+ ; $4FB = $40 if you try to disable MT!
|
||||
lda #$30 ; Bug in firmware? We're not sure yet.
|
||||
sta $4FB ; Any way, set it manually.
|
||||
:
|
||||
|
||||
;; Draw the name
|
||||
name: jsr space
|
||||
jsr update_curr_ptr
|
||||
loop: iny
|
||||
lda (curr_ptr),y
|
||||
jsr ascii_cout
|
||||
cpy curr_len
|
||||
bcc loop
|
||||
|
||||
space: lda #HI(' ')
|
||||
bne cout ; implicit RTS
|
||||
.endproc
|
||||
|
||||
home: lda #HI(ASCII_EM) ; move cursor to top left
|
||||
|
||||
;; Sets high bit before calling COUT
|
||||
ascii_cout:
|
||||
ora #$80
|
||||
cout: jmp COUT
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc do_open
|
||||
MLI_CALL OPEN, open_params
|
||||
lda open_params_ref_num
|
||||
sta read_params_ref_num
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.proc do_read
|
||||
MLI_CALL READ, read_params
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
text_resources := *
|
||||
|
||||
.proc help_string
|
||||
scrcode "RETURN: Select | TAB: Chg Vol | ESC: Back"
|
||||
.byte 0
|
||||
.endproc
|
||||
|
||||
;; Mousetext sequence: Enable, folder left, folder right, disable
|
||||
.proc folder_string
|
||||
.byte $0F,$1B,$D8,$D9,$18,$0E
|
||||
.byte 0 ; null terminated
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
DEFINE_OPEN_PARAMS open_params, prefix, $1C00
|
||||
open_params_ref_num := open_params::ref_num
|
||||
|
||||
DEFINE_CLOSE_PARAMS close_params
|
||||
|
||||
DEFINE_ON_LINE_PARAMS on_line_params, $60, prefix+1
|
||||
on_line_params_unit := on_line_params::unit_num
|
||||
|
||||
DEFINE_SET_PREFIX_PARAMS set_prefix_params, prefix
|
||||
|
||||
DEFINE_READ_PARAMS read_params, read_buffer, 0
|
||||
read_params_ref_num := read_params::ref_num
|
||||
read_params_request := read_params::request_count
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.endproc
|
||||
.assert .sizeof(selector) <= max_size, error, .sprintf("Must fit in $300 bytes, is: $%x", .sizeof(selector))
|
||||
install_size = .sizeof(selector)
|
||||
poporg
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
663
selectors/bye.system.s
Normal file
663
selectors/bye.system.s
Normal file
|
@ -0,0 +1,663 @@
|
|||
;;; Disassembly of the ProDOS 8 Selector ("BYE") found in ProDOS 1.9
|
||||
;;; and 2.0.x and installed automatically for 80-column systems.
|
||||
;;;
|
||||
;;; Installer wrapper added by Joshua Bell inexorabletash@gmail.com
|
||||
|
||||
.setcpu "65C02"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
.include "../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; ProDOS Technical Reference Manual, 5.1.5.2:
|
||||
;;;
|
||||
;;; ProDOS MLI call $65, the QUIT call, moves addresses $D100 through
|
||||
;;; $D3FF from the second 4K bank of RAM of the language card to
|
||||
;;; $1000, and executes a JMP to $1000. What initially resides in that
|
||||
;;; area is Apple's dispatcher code.
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Installer
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
max_size = $300
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
src := install_src
|
||||
end := install_src + install_size
|
||||
dst := $D100 ; Install location in ProDOS (bank 2)
|
||||
|
||||
src_ptr := $19
|
||||
dst_ptr := $1B
|
||||
|
||||
sta ALTZPOFF
|
||||
lda ROMIN
|
||||
lda ROMIN
|
||||
lda #>src
|
||||
sta src_ptr+1
|
||||
lda #<src
|
||||
sta src_ptr
|
||||
lda #>dst
|
||||
sta dst_ptr+1
|
||||
lda #<dst
|
||||
sta dst_ptr
|
||||
|
||||
loop: lda (src_ptr)
|
||||
sta (dst_ptr)
|
||||
inc src_ptr
|
||||
bne :+
|
||||
inc src_ptr+1
|
||||
: inc dst_ptr
|
||||
bne :+
|
||||
inc dst_ptr+1
|
||||
: lda src_ptr+1
|
||||
cmp #>end
|
||||
bne loop
|
||||
lda src_ptr
|
||||
cmp #<end
|
||||
bne loop
|
||||
lda (src_ptr)
|
||||
sta (dst_ptr)
|
||||
sta ALTZPOFF
|
||||
sta ROMINWB1
|
||||
sta ROMINWB1
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Selector
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
install_src := *
|
||||
|
||||
pushorg $1000
|
||||
.proc bbb
|
||||
|
||||
prefix := $280 ; length-prefixed
|
||||
|
||||
filenames := $1400 ; each is length + 15 bytes
|
||||
read_buffer := $2000 ; Also, start location for launched SYS files
|
||||
|
||||
mark_params := $60
|
||||
mark_ref_num := $61
|
||||
mark_position := $62 ; 3-bytes
|
||||
|
||||
next_device_num := $65 ; next device number to try
|
||||
|
||||
current_entry := $67 ; index of current entry
|
||||
num_entries := $68 ; length of |filenames|
|
||||
curr_len := $69 ; length of current entry name
|
||||
curr_ptr := $6C ; address of current entry name (in |filenames|)
|
||||
|
||||
prefix_depth := $6B ; 0 = root
|
||||
|
||||
entry_length := $6E
|
||||
entries_per_block := $6F
|
||||
file_count := $70 ; 2 bytes
|
||||
entry_num := $72
|
||||
page_start := $73 ; index of first entry shown on screen
|
||||
|
||||
max_entries := 128 ; max # of entries; more are ignored
|
||||
types_table := $74 ; high bit clear = dir, set = sys
|
||||
|
||||
top_row := 2 ; first row used on screen
|
||||
bottom_row := 21 ; last row used on screen
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
cld ; ProDOS protocol for QUIT routine
|
||||
lda ROMIN2 ; Page in ROM for reads, writes ignored
|
||||
|
||||
;; Point reset vector at this routine
|
||||
stz RESETVEC
|
||||
lda #>bbb
|
||||
sta RESETVEC+1
|
||||
jsr SETPWRC ; update validity check byte
|
||||
|
||||
lda #$A0
|
||||
jsr SLOT3 ; Activate 80-Column Firmware
|
||||
|
||||
;; Update system bitmap
|
||||
ldx #BITMAP_SIZE-1 ; zero it all out
|
||||
: stz BITMAP,x
|
||||
dex
|
||||
bpl :-
|
||||
inc BITMAP+BITMAP_SIZE-1 ; protect ProDOS global page
|
||||
lda #%11001111 ; protect zp, stack, text page 1
|
||||
sta BITMAP
|
||||
|
||||
;; Find device
|
||||
lda #2
|
||||
sta mark_params
|
||||
ldx DEVCNT ; max device num
|
||||
stx next_device_num
|
||||
lda DEVNUM
|
||||
bne check_device
|
||||
|
||||
next_device:
|
||||
ldx next_device_num
|
||||
lda DEVLST,x
|
||||
cpx #1
|
||||
bcs :+
|
||||
ldx DEVCNT
|
||||
inx
|
||||
: dex
|
||||
stx next_device_num
|
||||
|
||||
check_device:
|
||||
sta on_line_params_unit
|
||||
MLI_CALL ON_LINE, on_line_params
|
||||
bcs next_device
|
||||
|
||||
stz prefix_depth
|
||||
lda prefix+1
|
||||
and #$0F
|
||||
beq next_device
|
||||
adc #2
|
||||
tax
|
||||
|
||||
;; Resize prefix to length x and open the directory for reading
|
||||
.proc resize_prefix_and_open
|
||||
stx prefix
|
||||
lda #'/'
|
||||
sta prefix+1
|
||||
sta prefix,x
|
||||
stz prefix+1,x
|
||||
|
||||
MLI_CALL OPEN, open_params
|
||||
bcc :+
|
||||
|
||||
;; Open failed
|
||||
lda prefix_depth ; root?
|
||||
beq next_device
|
||||
jsr BELL1 ; no, but failed; beep
|
||||
jsr pop_prefix ; and go up a level
|
||||
stx prefix
|
||||
jmp keyboard_loop
|
||||
|
||||
|
||||
;; Open succeeded
|
||||
: inc prefix_depth
|
||||
stz num_entries
|
||||
lda open_params_ref_num
|
||||
sta read_params_ref_num
|
||||
sta mark_ref_num
|
||||
lda #.sizeof(SubdirectoryHeader)
|
||||
sta read_params_request
|
||||
stz read_params_request+1
|
||||
jsr do_read
|
||||
bcs finish_read2
|
||||
|
||||
;; Store entry_length/entries_per_block/file_count
|
||||
ldx #3
|
||||
: lda read_buffer + SubdirectoryHeader::entry_length,x
|
||||
sta entry_length,x
|
||||
dex
|
||||
bpl :-
|
||||
|
||||
sta read_params_request
|
||||
lda #1
|
||||
sta entry_num
|
||||
stz mark_position+1
|
||||
stz mark_position+2
|
||||
|
||||
lda file_count
|
||||
ora file_count+1
|
||||
bne next_file_entry ; any files?
|
||||
|
||||
finish_read2:
|
||||
bra finish_read
|
||||
|
||||
next_file_entry:
|
||||
bit file_count+1 ; wrap around?
|
||||
bmi finish_read2
|
||||
|
||||
|
||||
;; TODO: The math here is mysterious; understand and document
|
||||
floop: lda mark_position+1
|
||||
and #$FE
|
||||
sta mark_position+1
|
||||
ldy entry_num
|
||||
lda #0
|
||||
cpy entries_per_block
|
||||
bcc :+
|
||||
tay
|
||||
sty entry_num
|
||||
|
||||
inc mark_position+1
|
||||
carry: inc mark_position+1
|
||||
: dey
|
||||
clc
|
||||
bmi :+
|
||||
adc entry_length
|
||||
bcc :-
|
||||
bcs carry
|
||||
|
||||
: adc #4
|
||||
sta mark_position
|
||||
MLI_CALL SET_MARK, mark_params
|
||||
bcs finish_read2
|
||||
jsr do_read
|
||||
bcs finish_read2
|
||||
|
||||
inc entry_num
|
||||
lda read_buffer + FileEntry::storage_type_name_length
|
||||
and #$F0 ; mask off storage_type
|
||||
beq floop ; inactive file entry
|
||||
dec file_count
|
||||
bne :+
|
||||
dec file_count+1
|
||||
|
||||
;; Check read access
|
||||
: ror read_buffer + FileEntry::access
|
||||
bcc next_file_entry
|
||||
|
||||
;; Check file type
|
||||
lda read_buffer + FileEntry::file_type
|
||||
cmp #FT_DIRECTORY
|
||||
beq :+
|
||||
cmp #FT_SYSTEM
|
||||
bne next_file_entry
|
||||
|
||||
;; Check to see if we have room
|
||||
: ldx num_entries
|
||||
cpx #max_entries
|
||||
bcs finish_read
|
||||
|
||||
;; Store type
|
||||
sta types_table,x
|
||||
|
||||
;; Copy name
|
||||
jsr update_curr_ptr
|
||||
ldy #$0F ; name length + 1 (includes length byte)
|
||||
: lda read_buffer,y
|
||||
sta (curr_ptr),y
|
||||
dey
|
||||
bpl :-
|
||||
iny ; Y = 0
|
||||
and #$0F ; mask off name length (remove storage_type)
|
||||
sta (curr_ptr),y ; store length
|
||||
|
||||
;; Next
|
||||
inc num_entries
|
||||
bne next_file_entry
|
||||
|
||||
next: jmp next_device
|
||||
|
||||
finish_read:
|
||||
MLI_CALL CLOSE, close_params
|
||||
bcs next
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc draw_screen
|
||||
jsr SETTXT ; TEXT
|
||||
jsr HOME ; HOME
|
||||
lda #23 ; VTAB 23
|
||||
jsr TABV
|
||||
|
||||
;; Print help text
|
||||
ldy #0
|
||||
lda #20 ; HTAB 20
|
||||
jsr cout_string_hpos
|
||||
|
||||
;; Draw prefix
|
||||
jsr home
|
||||
ldx #0
|
||||
: lda prefix+1,x
|
||||
beq :+
|
||||
jsr ascii_cout
|
||||
inx
|
||||
bne :-
|
||||
|
||||
: stz current_entry
|
||||
stz page_start
|
||||
lda num_entries
|
||||
beq keyboard_loop ; no entries (empty directory)
|
||||
|
||||
row_count := $6A
|
||||
|
||||
cmp #bottom_row ; more entries than fit?
|
||||
bcc :+
|
||||
lda #(bottom_row - top_row + 1)
|
||||
: sta row_count
|
||||
lda #2
|
||||
sta WNDTOP
|
||||
sta WNDLFT
|
||||
lda #22
|
||||
sta WNDWDTH
|
||||
sta WNDBTM
|
||||
loop: jsr draw_current_line
|
||||
inc current_entry
|
||||
dec row_count
|
||||
bne loop
|
||||
stz current_entry
|
||||
beq draw_current_line_inv
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_up
|
||||
jsr draw_current_line ; clear inverse selection
|
||||
|
||||
ldx current_entry
|
||||
beq draw_current_line_inv ; first one? just redraw
|
||||
dec current_entry ; go to previous
|
||||
|
||||
lda CV
|
||||
cmp #top_row ; at the top?
|
||||
bne draw_current_line_inv ; if not, just draw
|
||||
dec page_start ; yes, adjust page and
|
||||
lda #ASCII_SYN ; scroll screen up
|
||||
bne draw_current_line_with_char
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_down
|
||||
jsr draw_current_line ; clear inverse selection
|
||||
|
||||
ldx current_entry
|
||||
inx
|
||||
cpx num_entries ; past the limit?
|
||||
bcs draw_current_line_inv ; yes, just redraw
|
||||
stx current_entry ; go to next
|
||||
|
||||
lda CV
|
||||
cmp #bottom_row ; at the bottom?
|
||||
bne draw_current_line_inv ; if not, just draw
|
||||
inc page_start ; yes, adjust page and
|
||||
lda #ASCII_ETB ; scroll screen down
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
draw_current_line_with_char:
|
||||
jsr COUT
|
||||
|
||||
draw_current_line_inv:
|
||||
jsr SETINV
|
||||
jsr draw_current_line
|
||||
;; fall through
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc keyboard_loop
|
||||
lda KBD
|
||||
bpl keyboard_loop
|
||||
sta KBDSTRB
|
||||
jsr SETNORM
|
||||
ldx num_entries
|
||||
beq :+ ; no up/down/return if empty
|
||||
|
||||
cmp #HI(ASCII_CR)
|
||||
beq on_return
|
||||
cmp #HI(ASCII_DOWN)
|
||||
beq on_down
|
||||
cmp #HI(ASCII_UP)
|
||||
beq on_up
|
||||
|
||||
: cmp #HI(ASCII_TAB)
|
||||
beq next_drive
|
||||
cmp #HI(ASCII_ESCAPE)
|
||||
bne keyboard_loop
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_escape
|
||||
jsr pop_prefix ; leaves length in X
|
||||
dec prefix_depth
|
||||
bra resize_prefix_and_open_jmp
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; Remove level from prefix; returns new length in X
|
||||
.proc pop_prefix
|
||||
ldx prefix
|
||||
loop: dex
|
||||
lda prefix,x
|
||||
cmp #'/'
|
||||
bne loop
|
||||
cpx #1
|
||||
bne done
|
||||
ldx prefix
|
||||
done: rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
next_drive:
|
||||
jmp next_device
|
||||
|
||||
inc_resize_prefix_and_open:
|
||||
inx
|
||||
|
||||
resize_prefix_and_open_jmp:
|
||||
jmp resize_prefix_and_open
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc on_return
|
||||
MLI_CALL SET_PREFIX, set_prefix_params
|
||||
bcs next_drive
|
||||
ldx current_entry
|
||||
jsr update_curr_ptr
|
||||
|
||||
ldx prefix
|
||||
: iny
|
||||
lda (curr_ptr),y
|
||||
inx
|
||||
sta prefix,x
|
||||
cpy curr_len
|
||||
bcc :-
|
||||
stx prefix
|
||||
|
||||
ldy current_entry
|
||||
lda types_table,y
|
||||
bpl inc_resize_prefix_and_open ; is directory???
|
||||
;; nope, system file, so...
|
||||
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc launch_sys_file
|
||||
jsr SETTXT
|
||||
jsr HOME
|
||||
lda #HI(ASCII_RIGHT) ; Right arrow
|
||||
jsr COUT
|
||||
|
||||
MLI_CALL OPEN, open_params
|
||||
bcs next_drive
|
||||
lda open_params_ref_num
|
||||
sta read_params_ref_num
|
||||
lda #$FF ; Load up to $FFFF bytes
|
||||
sta read_params_request
|
||||
sta read_params_request+1
|
||||
jsr do_read
|
||||
php
|
||||
MLI_CALL CLOSE, close_params
|
||||
plp
|
||||
bcs next_drive
|
||||
jmp read_buffer ; Invoke the loaded code
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
cout_string_hpos:
|
||||
sta CH
|
||||
|
||||
.proc cout_string
|
||||
loop: lda help_string,y
|
||||
beq done
|
||||
jsr COUT
|
||||
iny
|
||||
bne loop
|
||||
done: rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; Compute address/length of curr_ptr/curr_len
|
||||
;; Call with entry index in X.
|
||||
|
||||
.proc update_curr_ptr
|
||||
stz curr_ptr+1
|
||||
txa
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
asl a
|
||||
rol curr_ptr+1
|
||||
sta curr_ptr
|
||||
lda #>filenames
|
||||
clc
|
||||
adc curr_ptr+1
|
||||
sta curr_ptr+1
|
||||
ldy #0
|
||||
lda (curr_ptr),y
|
||||
sta curr_len
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc draw_current_line
|
||||
lda #2 ; hpos = 2
|
||||
sta COL80HPOS
|
||||
|
||||
ldx current_entry ; vpos = entry - page_start + 2
|
||||
txa
|
||||
sec
|
||||
sbc page_start
|
||||
inc a
|
||||
inc a
|
||||
jsr TABV
|
||||
|
||||
lda types_table,x
|
||||
bmi name ; is sys file?
|
||||
|
||||
;; Draw folder glyph
|
||||
stz COL80HPOS
|
||||
lda INVFLG
|
||||
pha
|
||||
ldy #(folder_string - string_start) ; Draw folder glyphs
|
||||
jsr cout_string
|
||||
pla
|
||||
sta INVFLG
|
||||
|
||||
;; Draw the name
|
||||
name: jsr space
|
||||
jsr update_curr_ptr
|
||||
loop: iny
|
||||
lda (curr_ptr),y
|
||||
jsr ascii_cout
|
||||
cpy curr_len
|
||||
bcc loop
|
||||
|
||||
space: lda #HI(' ')
|
||||
bne cout ; implicit RTS
|
||||
;; fall through
|
||||
.endproc
|
||||
|
||||
home: lda #HI(ASCII_EM) ; move cursor to top left
|
||||
|
||||
;; Sets high bit before calling COUT
|
||||
ascii_cout:
|
||||
ora #$80
|
||||
cout: jmp COUT
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc do_read
|
||||
MLI_CALL READ, read_params
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
string_start := *
|
||||
.proc help_string
|
||||
scrcode "RETURN: Select | TAB: Chg Vol | ESC: Back"
|
||||
.byte 0
|
||||
.endproc
|
||||
|
||||
;; Mousetext sequence: Enable, folder left, folder right, disable
|
||||
.proc folder_string
|
||||
.byte $0F,$1B,$D8,$D9,$18,$0E
|
||||
.byte 0 ; null terminated
|
||||
.endproc
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc open_params
|
||||
params: .byte 3
|
||||
path: .addr prefix
|
||||
buffer: .addr $1C00
|
||||
ref_num:.byte 0
|
||||
.endproc
|
||||
open_params_ref_num := open_params::ref_num
|
||||
|
||||
.proc close_params
|
||||
params: .byte 1
|
||||
ref_num:.byte 0
|
||||
.endproc
|
||||
|
||||
.proc on_line_params
|
||||
params: .byte 2
|
||||
unit: .byte $60
|
||||
buffer: .addr prefix+1
|
||||
.endproc
|
||||
on_line_params_unit := on_line_params::unit
|
||||
|
||||
.proc set_prefix_params
|
||||
params: .byte 1
|
||||
path: .addr prefix
|
||||
.endproc
|
||||
|
||||
.proc read_params
|
||||
params: .byte 4
|
||||
ref_num:.byte 1
|
||||
buffer: .word read_buffer
|
||||
request:.word 0
|
||||
trans: .word 0
|
||||
.endproc
|
||||
read_params_ref_num := read_params::ref_num
|
||||
read_params_request := read_params::request
|
||||
|
||||
.assert read_params::request - bbb <= $300, error, "Must fit in $300 bytes"
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.endproc
|
||||
install_size = $300
|
||||
poporg
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
499
selectors/selector.system.s
Normal file
499
selectors/selector.system.s
Normal file
|
@ -0,0 +1,499 @@
|
|||
;;; Disassembly of ProDOS QUIT handler (program selector)
|
||||
;;; This is a 40-column selector that prompts for prefix/pathname,
|
||||
;;; installed by default except on 80-column systems in 1.9 and
|
||||
;;; later.
|
||||
;;;
|
||||
;;; Installer wrapper added by Joshua Bell inexorabletash@gmail.com
|
||||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
.include "../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;;; ProDOS Technical Reference Manual, 5.1.5.2:
|
||||
;;;
|
||||
;;; ProDOS MLI call $65, the QUIT call, moves addresses $D100 through
|
||||
;;; $D3FF from the second 4K bank of RAM of the language card to
|
||||
;;; $1000, and executes a JMP to $1000. What initially resides in that
|
||||
;;; area is Apple's dispatcher code.
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Installer
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
max_size = $300
|
||||
|
||||
.proc maybe_install_driver
|
||||
|
||||
src := install_src
|
||||
end := install_src + install_size
|
||||
dst := $D100 ; Install location in ProDOS (bank 2)
|
||||
|
||||
src_ptr := $19
|
||||
dst_ptr := $1B
|
||||
|
||||
sta ALTZPOFF
|
||||
lda ROMIN
|
||||
lda ROMIN
|
||||
lda #>src
|
||||
sta src_ptr+1
|
||||
lda #<src
|
||||
sta src_ptr
|
||||
lda #>dst
|
||||
sta dst_ptr+1
|
||||
lda #<dst
|
||||
sta dst_ptr
|
||||
|
||||
loop: ldy #0
|
||||
lda (src_ptr),y
|
||||
sta (dst_ptr),y
|
||||
inc src_ptr
|
||||
bne :+
|
||||
inc src_ptr+1
|
||||
: inc dst_ptr
|
||||
bne :+
|
||||
inc dst_ptr+1
|
||||
: lda src_ptr+1
|
||||
cmp #>end
|
||||
bne loop
|
||||
lda src_ptr
|
||||
cmp #<end
|
||||
bne loop
|
||||
lda (src_ptr),y
|
||||
sta (dst_ptr),y
|
||||
sta ALTZPOFF
|
||||
sta ROMINWB1
|
||||
sta ROMINWB1
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Selector
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
install_src := *
|
||||
|
||||
pushorg $1000
|
||||
|
||||
.proc selector
|
||||
|
||||
PREFIX := $280
|
||||
|
||||
;;; ============================================================
|
||||
;;; Code
|
||||
;;; ============================================================
|
||||
|
||||
;; Page in normal banks, reset screen to 40 columns.
|
||||
lda ROMIN2
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
sta CLR80COL
|
||||
jsr SETNORM
|
||||
jsr INIT
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
|
||||
;; Clear system bitmap
|
||||
ldx #$17
|
||||
lda #$01
|
||||
sta BITMAP,x
|
||||
dex
|
||||
lda #$00
|
||||
: sta BITMAP,x
|
||||
dex
|
||||
bpl :-
|
||||
lda #$CF
|
||||
sta BITMAP
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc PromptForPrefix
|
||||
jsr HOME
|
||||
jsr CROUT
|
||||
ldx #msg_enter_prefix
|
||||
jsr ShowMessage
|
||||
lda #$03
|
||||
sta CV
|
||||
jsr CROUT
|
||||
MLI_CALL GET_PREFIX, get_prefix_params
|
||||
ldx PREFIX
|
||||
lda #$00
|
||||
sta $0281,x
|
||||
ldx PREFIX
|
||||
beq L105D
|
||||
L1052: lda PREFIX,x
|
||||
ora #$80
|
||||
sta $05FF,x
|
||||
dex
|
||||
bne L1052
|
||||
L105D: ldx #$00
|
||||
dec CV
|
||||
jsr CROUT
|
||||
|
||||
input_loop:
|
||||
jsr RDKEY
|
||||
cmp #HI(ASCII_CR)
|
||||
beq try_set_prefix
|
||||
pha
|
||||
jsr CLREOL
|
||||
pla
|
||||
cmp #HI(ASCII_ESCAPE)
|
||||
beq PromptForPrefix
|
||||
cmp #HI(ASCII_CLEAR)
|
||||
reprompt_for_prefix: ; used as a relay
|
||||
beq PromptForPrefix
|
||||
cmp #HI(ASCII_TAB)
|
||||
beq bad_prefix_key
|
||||
cmp #HI(ASCII_DELETE)
|
||||
beq :+
|
||||
cmp #HI(ASCII_LEFT)
|
||||
bne not_backspace
|
||||
: cpx #$00
|
||||
beq :+
|
||||
dec CH
|
||||
dex
|
||||
: jsr CLREOL
|
||||
jmp input_loop
|
||||
|
||||
not_backspace:
|
||||
bcs maybe_alphanumeric
|
||||
bad_prefix_key:
|
||||
jsr BELL
|
||||
jmp input_loop
|
||||
|
||||
maybe_alphanumeric:
|
||||
cmp #HI('Z')+1
|
||||
bcc :+
|
||||
and #%11011111 ; convert uppercase to lowercase
|
||||
: cmp #HI('.')
|
||||
bcc bad_prefix_key
|
||||
cmp #HI('Z')+1
|
||||
bcs bad_prefix_key
|
||||
cmp #HI('9')+1
|
||||
bcc :+
|
||||
cmp #HI('A')
|
||||
bcc bad_prefix_key
|
||||
|
||||
;; Place character, if it fits
|
||||
: inx
|
||||
cpx #39 ; screen max
|
||||
bcs reprompt_for_prefix
|
||||
sta PREFIX,x
|
||||
jsr COUT
|
||||
jmp input_loop
|
||||
|
||||
try_set_prefix:
|
||||
cpx #0
|
||||
beq PromptForPathname
|
||||
stx PREFIX
|
||||
MLI_CALL SET_PREFIX, get_prefix_params
|
||||
bcc PromptForPathname
|
||||
jsr BELL
|
||||
lda #$00
|
||||
|
||||
reprompt_for_prefix_relay:
|
||||
beq reprompt_for_prefix ; used as a relay
|
||||
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc PromptForPathname
|
||||
jsr HOME
|
||||
jsr CROUT
|
||||
ldx #msg_enter_pathname
|
||||
jsr ShowMessage
|
||||
reset_pathname:
|
||||
lda #3
|
||||
sta CV
|
||||
jsr CROUT
|
||||
ldx #0
|
||||
|
||||
input_loop:
|
||||
jsr RDKEY
|
||||
cmp #HI(ASCII_ESCAPE)
|
||||
bne :+
|
||||
lda CH
|
||||
bne PromptForPathname
|
||||
beq PromptForPrefix::reprompt_for_prefix_relay
|
||||
|
||||
: cmp #HI(ASCII_CLEAR)
|
||||
reprompt_for_pathname: ; used as a relay
|
||||
beq PromptForPathname
|
||||
cmp #HI(ASCII_TAB)
|
||||
beq bad_pathname_key
|
||||
cmp #HI(ASCII_DELETE)
|
||||
beq :+
|
||||
cmp #HI(ASCII_LEFT)
|
||||
bne not_backspace2
|
||||
: jmp backspace
|
||||
|
||||
not_backspace2:
|
||||
bcs maybe_alphanumeric2
|
||||
bad_pathname_key:
|
||||
jsr BELL
|
||||
jmp input_loop
|
||||
|
||||
maybe_alphanumeric2:
|
||||
cmp #HI(ASCII_CR)
|
||||
beq try_launch_file
|
||||
|
||||
cmp #HI('Z')+1
|
||||
bcc :+
|
||||
and #%11011111 ; convert uppercase to lowercase
|
||||
: cmp #HI('.')
|
||||
bcc bad_pathname_key
|
||||
cmp #HI('Z')+1
|
||||
bcs bad_pathname_key
|
||||
cmp #HI('9')+1
|
||||
bcc :+
|
||||
cmp #HI('A')
|
||||
bcc bad_pathname_key
|
||||
|
||||
;; Place character, if it fits
|
||||
: pha
|
||||
jsr CLREOL
|
||||
pla
|
||||
jsr COUT
|
||||
inx
|
||||
cpx #39 ; screen max
|
||||
bcs reprompt_for_pathname
|
||||
sta PREFIX,x
|
||||
jmp input_loop
|
||||
|
||||
;;; --------------------------------------------------
|
||||
|
||||
try_launch_file:
|
||||
lda #HI(' ')
|
||||
jsr COUT
|
||||
stx PREFIX
|
||||
MLI_CALL GET_FILE_INFO, get_file_info_params
|
||||
bcc :+
|
||||
jmp HandleError
|
||||
|
||||
: lda get_file_info_file_type
|
||||
cmp #FT_SYSTEM
|
||||
beq :+
|
||||
lda #$01
|
||||
jmp HandleError
|
||||
|
||||
: lda #$00
|
||||
sta close_ref_num
|
||||
MLI_CALL CLOSE, close_params
|
||||
bcc :+
|
||||
jmp HandleError
|
||||
|
||||
: lda get_file_info_access
|
||||
and #$01
|
||||
bne :+
|
||||
lda #ERR_IO_ERROR
|
||||
jmp HandleError
|
||||
|
||||
: MLI_CALL OPEN, open_params
|
||||
bcc :+
|
||||
jmp HandleError
|
||||
|
||||
: lda open_ref_num
|
||||
sta read_ref_num
|
||||
sta get_eof_ref_num
|
||||
MLI_CALL GET_EOF, get_eof_params
|
||||
bcs HandleError
|
||||
lda get_eof_eof+2
|
||||
beq :+
|
||||
lda #ERR_IO_ERROR
|
||||
bne HandleError
|
||||
|
||||
: lda get_eof_eof
|
||||
sta read_request_count
|
||||
lda get_eof_eof+1
|
||||
sta read_request_count+1
|
||||
MLI_CALL READ, read_params
|
||||
php
|
||||
MLI_CALL CLOSE, close_params
|
||||
bcc launch
|
||||
: plp
|
||||
bne HandleError
|
||||
plp
|
||||
launch: bcs :-
|
||||
jmp SYS_ADDR
|
||||
|
||||
backspace:
|
||||
lda CH
|
||||
beq :+
|
||||
dex
|
||||
lda #HI(' ')
|
||||
jsr COUT
|
||||
dec CH
|
||||
dec CH
|
||||
jsr COUT
|
||||
dec CH
|
||||
: jmp input_loop
|
||||
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc ShowMessage
|
||||
loop: lda message_table,x
|
||||
beq done
|
||||
jsr COUT
|
||||
inx
|
||||
bne loop
|
||||
done: rts
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.proc HandleError
|
||||
tmp := $DE
|
||||
|
||||
sta tmp
|
||||
lda #12
|
||||
sta CV
|
||||
jsr CROUT
|
||||
lda tmp
|
||||
|
||||
cmp #$01
|
||||
bne :+
|
||||
ldx #msg_not_sys
|
||||
bne show ; always
|
||||
|
||||
: cmp #ERR_INVALID_PATHNAME
|
||||
beq not_found
|
||||
cmp #ERR_PATH_NOT_FOUND
|
||||
beq not_found
|
||||
cmp #ERR_VOL_NOT_FOUND
|
||||
beq not_found
|
||||
cmp #ERR_FILE_NOT_FOUND
|
||||
beq not_found
|
||||
ldx #msg_io_error
|
||||
bne show
|
||||
|
||||
not_found:
|
||||
ldx #msg_not_found
|
||||
|
||||
show: jsr ShowMessage
|
||||
jmp PromptForPathname::reset_pathname
|
||||
.endproc
|
||||
|
||||
;;; ============================================================
|
||||
;;; Messages
|
||||
;;; ============================================================
|
||||
|
||||
message_table:
|
||||
|
||||
msg_enter_prefix := * - message_table
|
||||
scrcode "ENTER PREFIX (PRESS \"RETURN\" TO ACCEPT)"
|
||||
.byte 0
|
||||
|
||||
msg_enter_pathname := * - message_table
|
||||
scrcode "ENTER PATHNAME OF NEXT APPLICATION"
|
||||
.byte 0
|
||||
|
||||
msg_not_sys := * - message_table
|
||||
.byte HI(ASCII_BELL)
|
||||
scrcode "NOT A TYPE \"SYS\" FILE"
|
||||
.byte 0
|
||||
|
||||
msg_io_error := * - message_table
|
||||
.byte HI(ASCII_BELL)
|
||||
scrcode "I/O ERROR "
|
||||
.byte 0
|
||||
|
||||
msg_not_found := * - message_table
|
||||
.byte HI(ASCII_BELL)
|
||||
scrcode "FILE/PATH NOT FOUND "
|
||||
.byte 0
|
||||
|
||||
;;; ============================================================
|
||||
;;; ProDOS MLI Call Parameters
|
||||
;;; ============================================================
|
||||
|
||||
;;; GET_FILE_INFO params
|
||||
|
||||
get_file_info_params:
|
||||
.byte $A
|
||||
get_file_info_pathname:
|
||||
.addr PREFIX
|
||||
get_file_info_access:
|
||||
.byte 0
|
||||
get_file_info_file_type:
|
||||
.byte 0
|
||||
get_file_info_aux_type: .word 0
|
||||
get_file_info_storage_type: .byte 0
|
||||
get_file_info_blocks_used: .word 0
|
||||
get_file_info_mod_date: .word 0
|
||||
get_file_info_mod_time: .word 0
|
||||
get_file_info_create_date: .word 0
|
||||
get_file_info_create_time: .word 0
|
||||
|
||||
;;; OPEN params
|
||||
|
||||
open_params:
|
||||
.byte 3
|
||||
open_pathname:
|
||||
.addr PREFIX
|
||||
open_io_buffer:
|
||||
.addr $1800
|
||||
open_ref_num:
|
||||
.byte 0
|
||||
|
||||
;;; CLOSE params
|
||||
close_params:
|
||||
.byte 1
|
||||
close_ref_num:
|
||||
.byte 0
|
||||
|
||||
;;; READ params
|
||||
|
||||
read_params:
|
||||
.byte 4
|
||||
read_ref_num:
|
||||
.byte 0
|
||||
read_data_buffer:
|
||||
.addr SYS_ADDR
|
||||
read_request_count:
|
||||
.word 0
|
||||
read_trans_count:
|
||||
.word 0
|
||||
|
||||
;;; GET_EOF params
|
||||
|
||||
get_eof_params:
|
||||
.byte 2
|
||||
get_eof_ref_num:
|
||||
.byte 0
|
||||
get_eof_eof:
|
||||
.faraddr 0
|
||||
|
||||
;;; GET_PREFIX params
|
||||
|
||||
get_prefix_params:
|
||||
.byte 1
|
||||
get_prefix_pathname:
|
||||
.addr PREFIX
|
||||
|
||||
;;; ============================================================
|
||||
|
||||
.endproc
|
||||
install_size = $300
|
||||
poporg
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
35
setup/Makefile
Normal file
35
setup/Makefile
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/setup.system.SYS
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
30
setup/README.md
Normal file
30
setup/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# SETUP.SYSTEM
|
||||
|
||||
The November 1987 edition of Call-A.P.P.L.E features an article by Sean Nolan, ["SYSTEM.SETUP - A Proposed Startup File Standard"](https://www.callapple.org/magazines-4/call-a-p-p-l-e/setup-system-a-proposed-startup-file-standard/). The article was reprinted in [Beneath Apple DOS ProDOS 2020](https://archive.org/details/beneath-apple-dos-prodos-2020). The proposal combines the ProDOS-8 notion of running the first .SYSTEM file found on disk and the convention of chaining to the next .SYSTEM file, with the ProDOS-16 notion of enumerating a directory of startup files. A main `SETUP.SYSTEM` file is provided which enumerates all files in a `SETUPS/` directory. These "setup files" are BIN or SYS files which work like standard ProDOS-8 drivers. The advantages of this approach are:
|
||||
|
||||
* The top level directory only needs one SYSTEM file plus `SETUPS/`, which reduces clutter.
|
||||
* Each individual setup file is simpler than stand-alone SYSTEM files, as they don't need to implement chaining.
|
||||
|
||||
Which approach you use is a matter of taste.
|
||||
|
||||
## How do you use these?
|
||||
|
||||
If you choose this approach, use a tool like Copy II Plus or [Apple II DeskTop](https://github.com/a2stuff/a2d) to copy and arrange `SETUP.SYSTEM` as the first `.SYSTEM` file in your root directory. Create a `SETUPS/` directory, and copy the appropriate `.SETUPS` files there. A boot disk image catalog that is used on multiple different hardware configurations might include:
|
||||
|
||||
* `PRODOS` - the operating system, e.g. [ProDOS 2.4](https://prodos8.com/)
|
||||
* `SETUP.SYSTEM` - install No-Slot clock driver, if present
|
||||
* `QUIT.SYSTEM` - invoke the Quit handler immediately, as a program selector
|
||||
* `BASIC.SYSTEM` - which will not be automatically invoked, but is available to manually invoke
|
||||
* `SETUPS/NS.CLOCK.SYSTEM` - install No-Slot clock driver, if present
|
||||
* `SETUPS/ROMXRTC.SYSTEM` - install ROMX clock driver, if present
|
||||
* `SETUPS/FN.CLOCK.SYSTEM` - install FujiNet clock driver, if present
|
||||
* `SETUPS/DCLOCK.SYSTEM` - install DClock clock driver, if present
|
||||
* `SETUPS/CRICKET.SYSTEM` - install Cricket! clock driver, if present
|
||||
* `SETUPS/ZIPCHIP.SYSTEM` - slow the ZIP CHIP on speaker access, if present
|
||||
* `SETUPS/RAM.DRV.SYSTEM` - install RamWorks RAM disk driver, if present
|
||||
* `SETUPS/BUHBYE.SYSTEM` - install a customized Quit handler to replace the built-in one
|
||||
* `SETUPS/PAUSE.SYSTEM` - pause for a moment, so that you can inspect the output of the above
|
||||
|
||||
## Notes
|
||||
|
||||
The `SETUP.SYSTEM` program is not modified from the published version except that it no longer clears the screen between running each file in `SETUPS/`, so that any logged output remains visible.
|
329
setup/setup.system.s
Normal file
329
setup/setup.system.s
Normal file
|
@ -0,0 +1,329 @@
|
|||
;;;
|
||||
;;; SETUP.SYSTEM by Sean Nolan
|
||||
;;;
|
||||
;;; A Proposed Startup File Standard
|
||||
;;;
|
||||
;;; Published in Call-APPLE, November, 1987
|
||||
;;; This program is in the public domain.
|
||||
;;;
|
||||
;;; This program mimics the ProDOS 16
|
||||
;;; SYSTEM.SETUP convention. It can be used
|
||||
;;; to install RAM disk drivers, clock
|
||||
;;; drivers, and IIGS Classic Desk
|
||||
;;; Accessories on bootup under ProDOS 8.
|
||||
;;;
|
||||
;;; This program loads and calls all BINary
|
||||
;;; and SYStem files in a subdirectory named
|
||||
;;; SETUPS. It then looks for the second
|
||||
;;; system program in the volume directory
|
||||
;;; whose name ends in ".SYSTEM", and runs
|
||||
;;; that.
|
||||
;;;
|
||||
;;;
|
||||
|
||||
;;; Original code clears the screen before/after each driver. Skip it.
|
||||
NO_HOME = 1
|
||||
|
||||
.define ORG .org
|
||||
.define DFB .byte
|
||||
.define DA .addr
|
||||
.define DS .res
|
||||
.define ASC .byte
|
||||
.define ASCH scrcode
|
||||
.feature labels_without_colons +
|
||||
.feature loose_string_term +
|
||||
.include "apple2.mac"
|
||||
|
||||
.if 0
|
||||
TYP $FF ;save as a system file
|
||||
.endif
|
||||
ORG $BD00 ;load at $2000, but run at $BD00
|
||||
;;; ****************** equates
|
||||
CH = $24
|
||||
IN2 = $280
|
||||
FILETYPE = IN2+16
|
||||
AUXCODE = IN2+31
|
||||
RESET = $3F2
|
||||
IOBUFFER = $B900
|
||||
PRODOS = $BF00
|
||||
QUITVECT = $BF03
|
||||
DEVNUM = $BF30
|
||||
BITMAP = $BF58
|
||||
INIT = $FB2F
|
||||
VTABZ = $FC24
|
||||
HOME = $FC58
|
||||
RDKEY = $FD0C
|
||||
SETVID = $FE93
|
||||
SETKBD = $FE89
|
||||
SETNORM = $FE84
|
||||
;;; ****************** boot code
|
||||
VOLNAME = * ;The first 17 bytes are overwritten with the
|
||||
;name of the volume from which this was run.
|
||||
LDX #1 ;mark page $BD as free in the system bitmap
|
||||
STX BITMAP+23 ;so we can put Online result in our code.
|
||||
DEX ;relocate this program to $BD00-BEFF
|
||||
LOOP1 LDA $2000,X
|
||||
STA $BD00,X
|
||||
LDA $2100,X
|
||||
STA $BE00,X
|
||||
INX
|
||||
BNE LOOP1
|
||||
DEX
|
||||
TXS ;init stack pointer
|
||||
JMP ENTER ;jump to relocated code
|
||||
DIRNAME DFB 6 ;DirName and VolName must be in the same page
|
||||
ASCH "SETUPS"
|
||||
|
||||
;;; ****** Get name of boot volume
|
||||
ENTER LDA DEVNUM ;get name of last volume accessed
|
||||
STA ONLINEN
|
||||
JSR PRODOS
|
||||
DFB $C5 ;ONLINE
|
||||
DA ONLINEP
|
||||
LDA VOLNAME+1 ;insert a slash nefore the name
|
||||
AND #$0F
|
||||
TAX
|
||||
INX
|
||||
STX VOLNAME
|
||||
LDA #$2F ;/
|
||||
STA VOLNAME+1
|
||||
LDA QUITVECT+1 ;save original quit vector
|
||||
STA QUITMOD1+1
|
||||
LDA QUITVECT+2
|
||||
STA QUITMOD2+1
|
||||
;;; ****** Clean up before & after calling files
|
||||
MAINLOOP LDX #2 ;point Reset vector and ProDOS
|
||||
LOOP3 LDA JUMP+1,X ;Quit vectors to MainLoop
|
||||
STA RESET,X
|
||||
LDA JUMP,X
|
||||
STA QUITVECT,X
|
||||
DEX
|
||||
BPL LOOP3
|
||||
TXS ;fix stack pointer (X=$FF)
|
||||
JSR CLOSE ;close all open files
|
||||
LDX #23 ;clear system bit map
|
||||
LDA #0
|
||||
LOOP2 STA BITMAP,X
|
||||
DEX
|
||||
BPL LOOP2
|
||||
LDA #$CF ;mark pages 0,1,4-7 as used
|
||||
STA BITMAP
|
||||
LDA #%111 ;mark pages $BD-$BF as used
|
||||
STA BITMAP+23
|
||||
LDA $C082 ;Language card off
|
||||
STA $C00C ;40-column
|
||||
STA $C00E ;normal character set
|
||||
STA $C000 ;80STORE off
|
||||
JSR SETNORM ;normal
|
||||
JSR INIT ;display text page 1
|
||||
JSR SETVID ;PR#0
|
||||
JSR SETKBD ;IN#0
|
||||
;;; Make sure boot volume is around
|
||||
;;; AND set prefix to the boot volume
|
||||
.if NO_HOME
|
||||
VOLMOUNT
|
||||
.else
|
||||
VOLMOUNT JSR HOME
|
||||
.endif
|
||||
JSR PRODOS ;set prefix to volume
|
||||
DFB $C6 ;SET PREFIX
|
||||
DA PFX2P
|
||||
BCC VOLOK
|
||||
LDX #13
|
||||
LOOP6 LDA VOLTEXT-1,X ;print message "insert volume"
|
||||
STA $5A8+4,X
|
||||
DEX
|
||||
BNE LOOP6
|
||||
LOOP7 LDA VOLNAME+1,X ;print volume name
|
||||
ORA #$80
|
||||
STA $5A8+19,X
|
||||
INX
|
||||
CPX VOLNAME
|
||||
BCC LOOP7
|
||||
LDA #35 ;go to CH=35, CV=11
|
||||
STA CH
|
||||
LDA #11
|
||||
JSR VTABZ
|
||||
JSR RDKEY ;wait for keypress
|
||||
JMP VOLMOUNT
|
||||
;;; ****** Get name of next file at IN2
|
||||
VOLOK JSR NEXTFILE ;get name of next file at IN2
|
||||
BCS EXITLOOP ;if error, we're done with setup files
|
||||
;;; ****** Load and call setup file
|
||||
JSR PRODOS ;set prefix to SETUPS
|
||||
DFB $C6 ;SET PREFIX
|
||||
DA PFX1P
|
||||
JSR READFILE ;read in file whose name is at IN@
|
||||
;and call it if there was no error.
|
||||
JUMP JMP MAINLOOP ;3 bytes here copied into ProDOS quit vector
|
||||
DFB $BD^$A5 ;3 bytes here are copied into reset vector
|
||||
EXITLOOP INC RESET+2 ;scramble reset vector
|
||||
QUITMOD1 LDA #0 ;restore original quit vector
|
||||
STA QUITVECT+1
|
||||
QUITMOD2 LDA #0
|
||||
STA QUITVECT+2
|
||||
;;; ****** Look for second system program on disk
|
||||
LDA #0 ;modify NextFile routine so that it searches
|
||||
STA NUMBER+1 ;the volume directory for system files only.
|
||||
STA CHEKTYPE+1
|
||||
LDA #<VOLNAME ;NamePtr+1 does not bneed to be changed
|
||||
STA NAMEPTR ;since VolName and DirName are in the same page
|
||||
NEXTSYS JSR NEXTFILE
|
||||
BCS QUIT
|
||||
LDX IN2 ;see if file ends with ".SYSTEM"
|
||||
LDY #6
|
||||
LOOP4 LDA IN2,X ;I expect pathname at IN2 in low ASCII
|
||||
CMP SYSTEXT,Y
|
||||
BNE NEXTSYS
|
||||
DEX
|
||||
DEY
|
||||
BPL LOOP4
|
||||
INC MOD+1
|
||||
MOD LDA #$FF ;the first .SYSTEM program we find is this
|
||||
BEQ NEXTSYS ;one, so skip it and look for next one.
|
||||
JSR READFILE ;if successful, never come back
|
||||
QUIT JSR PRODOS
|
||||
DFB $65 ;QUIT
|
||||
DA QUITP
|
||||
SYSTEXT ASC '.SYSTEM'
|
||||
|
||||
;;; Get name of next system file or binary file
|
||||
;;;
|
||||
;;; This routine is set up to look for both SYSTEM and
|
||||
;;; BINary files in the SETUPs subdirectory. It is later
|
||||
;;; modified to search for SYSTEM files only in the
|
||||
;;; volume directory. The locations which are changed
|
||||
;;; are ChekType+1, Number+1, and NamePtr (in the Open
|
||||
;;; parametr list)
|
||||
;;;
|
||||
;;; Returns carry if not found, clear if found.
|
||||
NEXTFILE JSR PRODOS
|
||||
DFB $C8 ;OPEN
|
||||
DA OPENP
|
||||
BCS CLOSE
|
||||
LDA OPENN
|
||||
STA MARKN
|
||||
STA READN
|
||||
JSR PRODOS ;Read in first 39 bytes of directory to
|
||||
DFB $CA ;IN2. This gets the number of entries per
|
||||
DA READP ;block and number of bytes per entry.
|
||||
BCS CLOSE
|
||||
LDA IN2+35 ;save number of bytes per directory entry
|
||||
STA ENTSIZE+1
|
||||
LDA IN2+36 ;save number of entries per directory block
|
||||
STA ENTRIES+1
|
||||
NEXTENT INC NUMBER+1
|
||||
NUMBER LDA #0 ;self-modified operand
|
||||
;;; Retrieve catalog entry #A
|
||||
LDX #$FE ;build page index in X
|
||||
LOOP5 INX
|
||||
INX
|
||||
ENTRIES CMP #13
|
||||
BCC OK
|
||||
SBC ENTRIES+1
|
||||
BCS LOOP5 ;always
|
||||
OK TAY
|
||||
LDA #4 ;1st entry per directory block starts 4 bytes in
|
||||
LOOP10 DEY
|
||||
BMI OK2
|
||||
CLC
|
||||
ENTSIZE ADC #39 ;add size of directory entry
|
||||
BCC LOOP10
|
||||
INX
|
||||
BNE LOOP10 ;always
|
||||
OK2 STA MARK ;save mark in file
|
||||
STX MARK+1
|
||||
JSR PRODOS ;set the mark
|
||||
DFB $CE ;SET_MARK
|
||||
DA MARKP
|
||||
BCS CLOSE
|
||||
JSR PRODOS ;read in directory info
|
||||
DFB $CA ;READ
|
||||
DA READP
|
||||
BCS CLOSE
|
||||
LDA IN2 ;make sure that file is not deleted
|
||||
BEQ NEXTENT
|
||||
AND #$0F
|
||||
STA IN2
|
||||
LDA FILETYPE ;make sure file type is correct
|
||||
EOR #$FF ;we look for system programs...
|
||||
BEQ CLOSE
|
||||
CHEKTYPE EOR 6^$FF ;...and binary ones.
|
||||
BNE NEXTENT
|
||||
CLOSE PHP ;close all files - do not change carry
|
||||
JSR PRODOS
|
||||
DFB $CC ;CLOSE
|
||||
DA CLOSEP
|
||||
PLP
|
||||
ANRTS RTS
|
||||
;;; Read file and call it.
|
||||
;;; Name should be found at IN2
|
||||
;;; Prefix must be set.
|
||||
READFILE LDX FILETYPE ;if a system program, set to read to $2000
|
||||
LDA #$20
|
||||
INX
|
||||
BEQ SETDEST
|
||||
LDX AUXCODE ;else, set to read in file at address
|
||||
LDA AUXCODE+1 ;found in auxcode
|
||||
SETDEST STX READ2D
|
||||
STA READ2D+1
|
||||
JSR PRODOS ;Open file
|
||||
DFB $C8 ;OPEN
|
||||
DA OPEN2P
|
||||
BCS CLOSE
|
||||
LDA OPEN2N
|
||||
STA READ2N
|
||||
JSR PRODOS ;Read file into memory
|
||||
DFB $CA ;READ
|
||||
DA READ2P
|
||||
JSR CLOSE
|
||||
BCS ANRTS
|
||||
JMP (READ2D) ;call the file just loaded
|
||||
;;; ****** ProDOS MLI parameter lists
|
||||
ONLINEP DFB 2 ;Online parameter list
|
||||
ONLINEN DS 1
|
||||
DA VOLNAME+1
|
||||
;;;
|
||||
PFX1P DFB 1 ;to set prefix to SETUP
|
||||
DA DIRNAME
|
||||
;;;
|
||||
PFX2P DFB 1 ;to set prefix to volume directory
|
||||
DA VOLNAME
|
||||
;;;
|
||||
QUITP DFB 4,0,0,0,0,0,0
|
||||
|
||||
;;;
|
||||
CLOSEP DFB 1,0 ;close all files
|
||||
;;;
|
||||
OPENP DFB 3 ;open directory
|
||||
NAMEPTR DA DIRNAME ;pathname pointer
|
||||
DA IOBUFFER
|
||||
OPENN DS 1 ;reference number
|
||||
;;;
|
||||
MARKP DFB 2 ;set mark in directory
|
||||
MARKN DS 1
|
||||
MARK DS 3
|
||||
;;;
|
||||
READP DFB 4 ;read directory
|
||||
READN DS 1
|
||||
DA IN2 ;target address
|
||||
DA 39 ;length
|
||||
DS 2
|
||||
;;;
|
||||
OPEN2P DFB 3 ;open setup or system file
|
||||
DA IN2
|
||||
DA IOBUFFER
|
||||
OPEN2N DS 1
|
||||
;;;
|
||||
READ2P DFB 4 ;read setup or system file
|
||||
READ2N DS 1
|
||||
READ2D DS 2 ;destination of file is self-mod here
|
||||
DA $B900-$800 ;ask for largest possible that will fit
|
||||
DS 2
|
||||
;;;
|
||||
VOLTEXT ASCH "INSERT VOLUME"
|
||||
.if 0
|
||||
CHK ;checksum - eor for all previous bytes
|
||||
.else
|
||||
.byte $E8
|
||||
.endif
|
53
textcolors/Makefile
Normal file
53
textcolors/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/a2green.system.SYS \
|
||||
$(OUTDIR)/a2green.setup.SYS \
|
||||
$(OUTDIR)/bw.system.SYS \
|
||||
$(OUTDIR)/bw.setup.SYS \
|
||||
$(OUTDIR)/deepblue.system.SYS \
|
||||
$(OUTDIR)/deepblue.setup.SYS \
|
||||
$(OUTDIR)/gray.system.SYS \
|
||||
$(OUTDIR)/gray.setup.SYS \
|
||||
$(OUTDIR)/gsblue.system.SYS \
|
||||
$(OUTDIR)/gsblue.setup.SYS \
|
||||
$(OUTDIR)/mint.system.SYS \
|
||||
$(OUTDIR)/mint.setup.SYS \
|
||||
$(OUTDIR)/pink.system.SYS \
|
||||
$(OUTDIR)/pink.setup.SYS \
|
||||
$(OUTDIR)/wb.system.SYS \
|
||||
$(OUTDIR)/wb.setup.SYS
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.BIN $(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
35
textcolors/README.md
Normal file
35
textcolors/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Text Color Utilities
|
||||
|
||||
Author: @chrisparana
|
||||
|
||||
These utilities will work with the Apple IIgs or on any Apple II equipped with a VidHD card. Each will set the color of the text, background, and border and then invoke the ProDOS quit handler immediately.
|
||||
|
||||
Useful if you'd like to automatically set a theme at boot or select one manually afterwards. Originally created because I wanted my GS to use a different set of colors when booting my ProDOS 8 partition then what I have set as default in the Control Panel.
|
||||
|
||||
To have these themes applied at boot, place one of the theme SYSTEM at the end of your load chain.
|
||||
|
||||
---
|
||||
|
||||
[A2GREEN.SYSTEM](a2green.system.s)
|
||||
* Apple Monitor II green phosphor theme
|
||||
|
||||
[BW.SYSTEM](bw.system.s)
|
||||
* White text on black background
|
||||
|
||||
[DEEPBLUE.SYSTEM](deepblue.system.s)
|
||||
* White text on deep blue background
|
||||
|
||||
[GRAY.SYSTEM](gray.system.s)
|
||||
* Dark gray text on light gray background
|
||||
|
||||
[GSBLUE.SYSTEM](gsblue.system.s)
|
||||
* The Apple IIgs system defaults
|
||||
|
||||
[MINT.SYSTEM](mint.system.s)
|
||||
* A minty flavored theme
|
||||
|
||||
[PINK.SYSTEM](pink.system.s)
|
||||
* Dark gray text on a pink background with light blue borders
|
||||
|
||||
[WB.SYSTEM](wb.system.s)
|
||||
* Black text on a black background
|
25
textcolors/a2green.system.s
Normal file
25
textcolors/a2green.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$C0 ; text: bright greeen on black
|
||||
sta TBCOLOR
|
||||
lda #$00 ; border: black
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/bw.system.s
Normal file
25
textcolors/bw.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$0f ; text: black on white
|
||||
sta TBCOLOR
|
||||
lda #$0f ; border: white
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/deepblue.system.s
Normal file
25
textcolors/deepblue.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$f2 ; text: white on dark blue
|
||||
sta TBCOLOR
|
||||
lda #$02 ; text: dark blue
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/gray.system.s
Normal file
25
textcolors/gray.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$5a ; text: dark gray on light gray
|
||||
sta TBCOLOR
|
||||
lda #$0a ; border: gray
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/gsblue.system.s
Normal file
25
textcolors/gsblue.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$f6 ; border: white on medium blue
|
||||
sta TBCOLOR
|
||||
lda #$06 ; border: medium blue
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/mint.system.s
Normal file
25
textcolors/mint.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$2e ; text: dark blue on bright green
|
||||
sta TBCOLOR
|
||||
lda #$0e ; border: bright green
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/pink.system.s
Normal file
25
textcolors/pink.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$5b ; text: gray on pink
|
||||
sta TBCOLOR
|
||||
lda #$07 ; border: light blue
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
25
textcolors/wb.system.s
Normal file
25
textcolors/wb.system.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
.setcpu "6502"
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
lda #$f0 ; text: white on black
|
||||
sta TBCOLOR
|
||||
lda #$00 ; border: black
|
||||
sta CLOCKCTL
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
46
util/Makefile
Normal file
46
util/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
OUTDIR = ../out
|
||||
|
||||
HEADERS = $(wildcard *.inc) $(wildcard ../inc/*.inc)
|
||||
|
||||
TARGETS = \
|
||||
$(OUTDIR)/quit.system.SYS \
|
||||
$(OUTDIR)/pause.system.SYS \
|
||||
$(OUTDIR)/pause.setup.SYS \
|
||||
$(OUTDIR)/me.first.system.SYS \
|
||||
$(OUTDIR)/me.first.setup.SYS \
|
||||
$(OUTDIR)/date.BIN
|
||||
|
||||
# For timestamps
|
||||
MM = $(shell date "+%-m")
|
||||
DD = $(shell date "+%-d")
|
||||
YY = $(shell date "+%-y")
|
||||
DEFINES = -D DD=$(DD) -D MM=$(MM) -D YY=$(YY)
|
||||
|
||||
XATTR := $(shell command -v xattr 2> /dev/null)
|
||||
|
||||
.PHONY: clean all
|
||||
all: $(OUTDIR) $(TARGETS)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir -p $(OUTDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)/*.o
|
||||
rm -f $(OUTDIR)/*.list
|
||||
rm -f $(TARGETS)
|
||||
|
||||
$(OUTDIR)/%.o: %.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.setup.o: %.system.s $(HEADERS)
|
||||
ca65 $(CAFLAGS) $(DEFINES) -D BUILD_SETUP_FILE --listing $(basename $@).list -o $@ $<
|
||||
|
||||
$(OUTDIR)/%.BIN $(OUTDIR)/%.SYS: $(OUTDIR)/%.o
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
ifdef XATTR
|
||||
xattr -wx prodos.AuxType '00 20' $@
|
||||
endif
|
10
util/README.md
Normal file
10
util/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Utilities
|
||||
|
||||
* [DATE](date.s)
|
||||
* Prints the current ProDOS date/time, to verify the time is set and updating. Run from the BASIC prompt: `-DATE`
|
||||
* [QUIT.SYSTEM](quit.system.s)
|
||||
* This invokes the ProDOS quit handler immediately. It can be used as the last in a chain of "driver" installers to invoke the program selector, e.g. if you want to also keep `BASIC.SYSTEM` in your root directory but not launch it.
|
||||
* [PAUSE.SYSTEM](pause.system.s)
|
||||
* Waits for a fraction of a second before invoking the next driver file. Useful in case the log messages from the driver installers go by too quickly!
|
||||
* [ME.FIRST.SYSTEM](me.first.system.s)
|
||||
* Moves the current volume to the end of DEVLST. Niche, but useful in some circumstances.
|
|
@ -1,31 +1,32 @@
|
|||
;;; Query ProDOS and print the current date/time
|
||||
|
||||
;;; Output is: MM/DD/YY HH:MM
|
||||
|
||||
.setcpu "6502"
|
||||
.org $2000
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "common.inc"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
start:
|
||||
PRODOS_CALL MLI_GET_TIME, 0
|
||||
MLI_CALL GET_TIME, 0
|
||||
|
||||
jsr zstrout
|
||||
HIASCIIZ "Time: "
|
||||
;;; DATEHI ($BF91) DATELO ($BF90)
|
||||
;;; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
||||
;;; +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
;;; DATE: | year | month | day |
|
||||
;;; +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
;;;
|
||||
;;; TIMEHI ($BF93) TIMELO ($BF92)
|
||||
;;; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
||||
;;; +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
;;; TIME: |0 0 0| hour | |0 0| minute |
|
||||
;;; +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
|
||||
lda TIMELO+1 ; hour
|
||||
jsr cout_number
|
||||
|
||||
lda #HI(':') ; ':'
|
||||
jsr COUT
|
||||
|
||||
lda TIMELO ; minute
|
||||
jsr cout_number
|
||||
|
||||
jsr CROUT
|
||||
|
||||
jsr zstrout
|
||||
HIASCIIZ "Date: "
|
||||
;; Date
|
||||
|
||||
lda DATELO+1 ; month
|
||||
ror a
|
||||
|
@ -39,25 +40,44 @@ start:
|
|||
and #%00001111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
lda #HI('/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; day
|
||||
and #%00011111
|
||||
jsr cout_number
|
||||
|
||||
lda #(HI '/') ; /
|
||||
lda #HI('/') ; /
|
||||
jsr COUT
|
||||
|
||||
pla ; year
|
||||
jsr cout_number
|
||||
|
||||
lda #HI(' ')
|
||||
jsr COUT
|
||||
jsr COUT
|
||||
|
||||
;; Time
|
||||
|
||||
lda TIMELO+1 ; hour
|
||||
and #%00011111
|
||||
jsr cout_number
|
||||
|
||||
lda #HI(':') ; ':'
|
||||
jsr COUT
|
||||
|
||||
lda TIMELO ; minute
|
||||
and #%00111111
|
||||
jsr cout_number
|
||||
|
||||
jsr CROUT
|
||||
|
||||
rts
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
.proc cout_number
|
||||
ldx #(HI '0')
|
||||
ldx #HI('0')
|
||||
cmp #10 ; >= 10?
|
||||
bcc tens
|
||||
|
||||
|
@ -72,31 +92,7 @@ tens: pha
|
|||
jsr COUT
|
||||
|
||||
units: pla
|
||||
ora #(HI '0')
|
||||
ora #HI('0')
|
||||
jsr COUT
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.proc zstrout
|
||||
ptr := $A5
|
||||
|
||||
pla ; read address from stack
|
||||
sta ptr
|
||||
pla
|
||||
sta ptr+1
|
||||
bne skip ; always (since data not on ZP)
|
||||
|
||||
next: jsr COUT
|
||||
skip: inc ptr
|
||||
bne :+
|
||||
inc ptr+1
|
||||
: ldy #0
|
||||
lda (ptr),y
|
||||
bne next
|
||||
|
||||
lda ptr+1 ; restore address to stack
|
||||
pha
|
||||
lda ptr
|
||||
pha
|
||||
rts
|
||||
.endproc
|
48
util/me.first.system.s
Normal file
48
util/me.first.system.s
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
.proc maybe_install_driver
|
||||
;; Find the current device's index in the list
|
||||
ldx #0
|
||||
: lda DEVLST,x
|
||||
and #$F0
|
||||
cmp DEVNUM
|
||||
beq found
|
||||
inx
|
||||
cpx DEVCNT
|
||||
bcc :-
|
||||
bcs exit ; last one or not found
|
||||
|
||||
;; Save it
|
||||
found: ldy DEVLST,x
|
||||
|
||||
;; Move everything up
|
||||
: lda DEVLST+1,x
|
||||
sta DEVLST,x
|
||||
inx
|
||||
cpx DEVCNT
|
||||
bne :-
|
||||
|
||||
;; Place it at the end
|
||||
tya
|
||||
sta DEVLST,x
|
||||
|
||||
exit: rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
30
util/pause.system.s
Normal file
30
util/pause.system.s
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
.setcpu "6502"
|
||||
.linecont +
|
||||
.feature string_escapes
|
||||
|
||||
.include "apple2.inc"
|
||||
.include "apple2.mac"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/macros.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
.include "../inc/ascii.inc"
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_preamble.inc"
|
||||
;;; ************************************************************
|
||||
|
||||
|
||||
.proc maybe_install_driver
|
||||
ldx #4
|
||||
: lda #$FF
|
||||
jsr $FCA8 ; WAIT
|
||||
dex
|
||||
bne :-
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;;; ************************************************************
|
||||
.include "../inc/driver_postamble.inc"
|
||||
;;; ************************************************************
|
24
util/quit.system.s
Normal file
24
util/quit.system.s
Normal file
|
@ -0,0 +1,24 @@
|
|||
|
||||
.setcpu "6502"
|
||||
.include "apple2.inc"
|
||||
|
||||
.include "../inc/apple2.inc"
|
||||
.include "../inc/prodos.inc"
|
||||
|
||||
.org SYS_ADDR
|
||||
|
||||
cld
|
||||
bit ROMIN2
|
||||
sta CLR80VID
|
||||
sta CLRALTCHAR
|
||||
sta CLR80COL
|
||||
jsr SETVID
|
||||
jsr SETKBD
|
||||
jsr SETNORM
|
||||
jsr INIT
|
||||
jsr HOME
|
||||
|
||||
MLI_CALL QUIT, quit_params
|
||||
brk
|
||||
|
||||
DEFINE_QUIT_PARAMS quit_params
|
Loading…
Reference in New Issue
Block a user