Merge pull request #52 from pzembrod/x16-r47

Changes for release 6502-C64 3.9.6
This commit is contained in:
Philip Zembrod 2024-10-04 22:20:09 +02:00 committed by GitHub
commit f8dd7b31b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1151 additions and 129 deletions

View File

@ -19,6 +19,7 @@ test_files = $(wildcard tests/*.f*)
test_files_petscii = $(patsubst tests/%, cbmfiles/%, $(test_files))
test_logs = $(patsubst %, test-%.log, $(vf_flavours))
test_resuls = $(patsubst %, test-%.result, $(vf_flavours))
test_keyboard_input = some 12345 keys
release_zipfile = volksforth-6502-c64-release.zip
@ -35,6 +36,7 @@ clean:
rm -f *.log *.result *.golden
rm -f cbmfiles/c??-testbase
rm -f disks/scratch.d64 emulator/sdcard.img
rm -f tests/golden/mycore*.golden
# Convenience targets
@ -43,6 +45,8 @@ binaries: $(vf_binaries)
test: $(test_resuls)
alltests: test test-v4th-x16-tsk.result
test64: std64 blk64
blk64: test-v4thblk-c64.result
@ -62,6 +66,18 @@ run-testbase: emulator/testbase.T64
run-testbase16: emulator/testbase16.T64
VICE=xplus4 emulator/run-in-vice.sh testbase16
run-c64: emulator/v4th-c64.T64 $(all_src_files_petscii)
emulator/run-in-vice.sh v4th-c64
run-c16+: emulator/v4th-c16+.T64 $(all_src_files_petscii)
VICE=xplus4 emulator/run-in-vice.sh v4th-c16+
run-c16-: emulator/v4th-c16-.T64 $(all_src_files_petscii)
VICE=xplus4 emulator/run-in-vice.sh v4th-c16-
run-x16: cbmfiles/v4th-x16 $(all_src_files_petscii) | emulator/sdcard.img
emulator/run-in-x16emu.sh v4th-x16
release: tmp/$(release_zipfile) COPYING RELEASE_NOTES.md
rm -rf release
@ -124,57 +140,57 @@ cbmfiles/v4th-x16e:
# Core test targets
$(test_logs): $(test_files_petscii) emulator/run-in-vice.sh
$(test_logs): $(test_files_petscii) $(wildcard emulator/run-in-*.sh)
test-v4thblk-c64.log: emulator/v4thblk-c64.T64 disks/empty.d64
rm -f cbmfiles/test.log disks/scratch.d64
cp disks/empty.d64 disks/scratch.d64
DISK9=scratch emulator/run-in-vice.sh v4thblk-c64 \
"include run-blk-tests.fth\n1234567890"
"include run-blk-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4th-c64.log: emulator/v4th-c64.T64
rm -f cbmfiles/test.log
emulator/run-in-vice.sh v4th-c64 \
"include run-std-tests.fth\n1234567890"
"include run-std-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4thblk-c16+.log: emulator/v4thblk-c16+.T64 disks/empty.d64
rm -f cbmfiles/test.log disks/scratch.d64
cp disks/empty.d64 disks/scratch.d64
VICE=xplus4 DISK9=scratch emulator/run-in-vice.sh v4thblk-c16+ \
"include run-blk-tests.fth\n1234567890"
"include run-blk-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4th-c16+.log: emulator/v4th-c16+.T64
rm -f cbmfiles/test.log
VICE=xplus4 emulator/run-in-vice.sh v4th-c16+ \
"include run-std-tests.fth\n1234567890"
"include run-std-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4thblk-c16-.log: emulator/v4thblk-c16-.T64
rm -f cbmfiles/test.log
VICE=xplus4 emulator/run-in-vice.sh v4thblk-c16- \
"include run-min-tests.fth\n1234567890"
"include run-min-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4th-c16-.log: emulator/v4th-c16-.T64
rm -f cbmfiles/test.log
VICE=xplus4 emulator/run-in-vice.sh v4th-c16- \
"include run-std-tests.fth\n1234567890"
"include run-std-tests.fth\n$(test_keyboard_input)"
petscii2ascii cbmfiles/test.log $@
test-v4th-x16.log: cbmfiles/v4th-x16 emulator/sdcard.img
test-v4th-x16.log: cbmfiles/v4th-x16 | emulator/sdcard.img
rm -f cbmfiles/test.log
emulator/run-in-x16emu.sh v4th-x16 \
"include run-std-tests.fth\n1234567890"
"include run-std-tests.fth\n$(test_keyboard_input)"
mcopy -i emulator/sdcard.img ::TEST.LOG cbmfiles/test.log
petscii2ascii cbmfiles/test.log $@
test-v4th-x16e.log: cbmfiles/v4th-x16e emulator/sdcard.img
test-v4th-x16e.log: cbmfiles/v4th-x16e | emulator/sdcard.img
rm -f cbmfiles/test.log
emulator/run-in-x16emu.sh v4th-x16e \
"include run-std-tests.fth\n1234567890"
"include run-std-tests.fth\n$(test_keyboard_input)"
mcopy -i emulator/sdcard.img ::TEST.LOG cbmfiles/test.log
petscii2ascii cbmfiles/test.log $@
@ -190,35 +206,60 @@ emulator/sdcard.img: emulator/sdcard.sfdisk
mv $@.tmp $@
test-v4thblk-c64.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double block report-blk)
prelim mycore-echo coreext double block report-blk)
cat $^ > $@
test-v4th-c64.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double report-noblk)
prelim mycore-echo coreext double report-noblk)
cat $^ > $@
test-v4thblk-c16+.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double block report-blk)
prelim mycore-echo coreext double block report-blk)
cat $^ > $@
test-v4th-c16+.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double report-noblk)
prelim mycore-echo coreext double report-noblk)
cat $^ > $@
test-v4thblk-c16-.golden: $(patsubst %, tests/golden/%.golden, \
prelim core)
prelim mycore-echo)
cat $^ > $@
test-v4th-c16-.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double report-noblk)
prelim mycore-echo coreext double report-noblk)
cat $^ > $@
test-v4th-x16.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double report-noblk)
prelim mycore-noecho coreext double report-noblk)
cat $^ > $@
test-v4th-x16e.golden: $(patsubst %, tests/golden/%.golden, \
prelim core coreext double report-noblk)
prelim mycore-noecho coreext double report-noblk)
cat $^ > $@
tests/golden/mycore-echo.golden: tests/golden/core-template.golden
sed -e 's/TMPL_KEYS_ECHO_TMPL/$(test_keyboard_input)/' \
-e 's/TMPL_KEYS_RECEIVED_TMPL/$(test_keyboard_input)/' $< >$@
tests/golden/mycore-noecho.golden: tests/golden/core-template.golden
sed -e 's/TMPL_KEYS_ECHO_TMPL//' \
-e 's/TMPL_KEYS_RECEIVED_TMPL/$(test_keyboard_input)/' $< >$@
# Sample test with a changed input that would run the tasker
# even while waiting for keyboard input:
test-v4th-x16-tsk.log: cbmfiles/v4th-x16 \
$(test_files_petscii) $(wildcard emulator/run-in-*.sh) \
cbmfiles/6502asm.fth cbmfiles/trns6502asm.fth \
cbmfiles/vf-lbls-cbm.fth cbmfiles/x16input-tsk.fth \
| emulator/sdcard.img
rm -f cbmfiles/test.log
emulator/run-in-x16emu.sh v4th-x16 \
"include x16input-tsk.fth\ninclude run-min-tests.fth\n$(test_keyboard_input)"
mcopy -i emulator/sdcard.img ::TEST.LOG cbmfiles/test.log
petscii2ascii cbmfiles/test.log $@
test-v4th-x16-tsk.golden: $(patsubst %, tests/golden/%.golden, \
prelim mycore-echo)
cat $^ > $@
# Rules for building Forth binaries on top of the plain vanilla

View File

@ -1,6 +1,8 @@
# VolksForth 6502 C64 Releases
# VolksForth 6502-C64 Releases
Release notes of VolksForth for 6502 on CBM-like machines (C64/C16/X16)
Release notes of VolksForth 6502-C64 which is the flavour of VolksForth
running on CBM-like 6502 machines - currently the C64, the C16/Plus4 and the
Commander X16.
## Release content
@ -18,11 +20,44 @@ The latest release zip file `volksforth-6502-c64-release.zip` contains
* `v4th-x16e` - Commander X16 kernel with added X16Edit and
DOS commands
* `doc/` - documentation, the beginning of an English translation
of the original German VolksForth/UltraForth manual for C64 and C16,
with some X16 specifics added.
* [`tasker.md`](doc/tasker.md) - the chapter about the multitasker
* `src/` - sources
* `v4th*.fth` - the binaries' main files
* `vf-*.fth` - sources from which VolksForth
kernels are compiled
* further Forth sources
* `6502asm.fth` - the 6502 assembler, needed to compile `Code` words
* `trns6502asm.fth` - the transient 6502 assembler. It lives on the
heap and is removed by `clear`. This allows building applications that
have code words but don't carry the assembler itself after saving.
* `tmp6502asm.fth` - like the transient 6502 assembler, but living on the
tmpheap instead of the heap. See below for tmpheap.
* `rom-ram-c16.fth` - macros for C16 bank switching
* `rom-ram-c64.fth` - macros for C64 bank switching
* `tracer.fth` - the debugger
* `tasker.fth` - the multitasker
* `multitask.fth` - the small bit of assembly code needed by tasker.fth
* `taskdemo.fth` - a C64/C16 demo of the tasker
* `taskdemo-x16.fth` - a X16 demo of the tasker
* `x16input-tsk.fth`- The usual v4th X16 keyboard input uses the regular
screen editor for line input, so no task switches happen during line input.
This input implementation allows task switches during input but uses Kernal
variables and has a cursor bug after backspace.
* `cbmopen.fth` - Forth words for Kernal channel I/O
* `lists.fth` - two list utility words
* `profiler.fth` - *** [4d2023-04](https://forth-ev.de/wiki/res/lib/exe/fetch.php/vd-archiv:4d2023-04.pdf) (English)
* `tmpheap.fth` - the reference implementation of the tmpheap design
as described at
[SVFIG 04-2021](https://www.forth.org/svfig/kk/04-2021.html), in the
[4d2021-03](https://forth-ev.de/wiki/res/lib/exe/fetch.php/vd-archiv:4d2021-03.pdf) (German) and used
in [cc64](https://github.com/pzembrod/cc64/blob/master/src/cc64/cc64.fth#L11)
* `x16tmpheap.fth` - a X16 tmpheap implementation that uses banked ram
* `notmpheap.fth` - a null implementation that redirects the tmpheap
to the regular VolksForth heap
* `tc-base.fth` - loadfile for the resident part of the target compiler
* `src_petscii/` - the files from `src/` converted to PETSCII
@ -31,14 +66,82 @@ The latest release zip file `volksforth-6502-c64-release.zip` contains
* `tests_petscii/` - the files from `tests/` converted to PETSCII
## Versions
The following version descriptions are only valid for VolksForth 6502
C64 Releases. As of now (Dec 2023), the different VolksForth platforms
The following version descriptions are only valid for VolksForth 6502-C64
releases. As of now (June 2024), the different VolksForth platforms
(6502, 68k, 8080, 8086) don't have shared code or shared versioning.
### 6502-C64 3.9.6
Main topic of this release is the removal of all usages of Kernal variable
uses esp. in the X16 VolksForth kernel. Because the addresses of Kernal
variables can change between different X16 Kernal releases, their use was in
the past the main reason why VolksForth broke after new X16 ROM releases.
With Kernal variable access replaced by Kernal API calls or by emitting
control characters, the VolksForth kernel should now be much more robust
when the X16 Kernal changes.
A secondary topic if this release is more bundled Forth sources, which are
now also better described (see Release content above).
And a third topic is the beginning of an English translation of the
original German VolksForth/UltraForth manual for C64 and C16.
Since this release affects some multitasking behaviour, the translation
was started with the [chapter about the multitasker](doc/tasker.md).
Changes affecting only the X16 VolksForth kernel:
* Clearing the IOStatus is now possible through the ExtApi call ($FEAB, thanks
to mooinglemur for implementing this), so the dependency on the address of
IOStatus could be removed.
* The implementation of line input in EXPECT was changed from switching cursor
on and off and using GETIN to using BASIN - which automatically handles the
cursor and also makes use of the CBM screen editor. The Kernal var dependency
that came with switching the cursor on and off is now removed.
The downside is that the
cooperative multitasker now can't run tasks during line input. The old
tasker-compatible EXPECT is now available as separtate source
x16input-tsk.fth. For details on this see [tasker.md](doc/tasker.md).
* The clearing of the Kernal variables QtSw and Insrt after each
char written to the console via CHROUT most likely has no real purpose.
It was therefore removed from the X16 variant.
* Up to version 3.9.5, the VolksForth kernel used BkgPenCol (X16: $0376)
to set both background and pen color during initialization. It would be
great ot have e.g. an ExtApi function to set BkgPenCol, similar to the
X16 BASIC command `COLOR`. I'm planning to suggest this,
but for the time being setting the background and pen color is
instead done via PETSCII control codes instead. To reflect this change,
the CBM VolksForth word `INK-POT` has been renamed to `X16-INK-POT`,
and it contains only 3 values (border color code, background color PETSCII
control charcter, pen color PETSCII control character. This removes the
dependency on BkgPenCol and thereby on the last remaining Kernal variable
that was used by VolksForth.
Changes affecting all VolksForth kernels (C64, C16, X16):
* The direct clearing of MsgFlg (X16: $028d) at the beginning of
(busin and (busout was removed; it shouldn't have had any real purpose.
The cooperative multitasker was extracted from the original disk 3 (see
[`disks/vforth4_3.fth`](https://github.com/forth-ev/VolksForth/blob/master/6502/C64/disks/vforth4_3.fth))
into the files `tasker.fth, multitask.fth` and `taskdemo.fth`. The latter was
ported to the X16 as `taskdemo-x16.fth`.
`rom-ram-sys.fth` was split up into a C64 and a C16 flavour. There is no X16
equivalent; X16 bank switching poses challenges and opportunities completely
different from those on C16 and C64.
New sources added: `lists,fth, tasker.fth, multitask.fth,
taskdemo.fth, taskdemo-x16.fth, x16input-tsk.fth, tmp6502asm.fth,
x16tmpheap.fth, rom-ram-c16.fth, rom-ram-c64.fth`
### 6502-C64 3.9.5
This release adapts the X16 VolksForth to the R46 ROM.
It also adds an X16 binary with added words to invoke the
This release adapts the X16 VolksForth kernel to the R46 X16 ROM.
July 2024 note: version 3.9.5 also runs with the R47 ROM.
The release also adds an X16 binary with added words to invoke the
ROM-based X16Edit (XED), to list directories and files (DIR and CAT)
and to issue DOS commands and read the error channel (DOS).

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

589
6502/C64/doc/tasker.md Normal file
View File

@ -0,0 +1,589 @@
# The Multitasker
VolksForth comes with a simple but powerful multitasker which allows the
creation of printer spoolers, clocks, counters and other simple background
tasks.
The main characteristic of this multitasker is that it uses cooperative
multitasking instad of preemtive multitasking.
This means that each task has to explicitly yield
execution control and access to I/O devices to make them available for other
tasks. However, each task can choose when this happens, esp. when to yield
execution control. Of course this must happen often enough for all tasks
to work well. Execution control is yielded via the word PAUSE.
## Motivating Use Case
Block 74 of [Disk 3](../disks/vforth4_3.fth) of the original
VolksForth/UltraForth distribution contains a print spooler that creates a task
`Printspool` to print a range of source blocks via `pthru` in the background
while the user can still interact with the system in the foreground.
Caveat: The printer code hasn't yet been extracted into `.fth` files and may
need some adaption to current VolksForth versions and esp. to the X16.
In particular, source block printing doesn't make much sense with the X16 as of
now because block source compiling is currently only supported on the C64 and
C16/Plus4.
## Task Demo
A simpler task demo has been extracted from Disk 3's block 62 into
[src/taskdemo.fth](../src/taskdemo.fth).
### On C64 or C16/Plus4
```
include tasker.fth
include taskdemo.fth
```
will create a counter in the top left corner, counting down in the background
from 256 to 0. A fun way to see the time spent in the background task is to
type
```
words
```
while the counter is running: The speed of words listing will increase
noticably once the counter has run out.
### On the X16
On the X16 things are slightly different:
```
include tasker.fth
include taskdemo-x16.fth
```
will create a counter in the top left corner, counting down in the background
from 4096 to 0. However, unlike on C64/C16/Plus4 this counter doesn't run while
the cursor is prompting for user input. Pressing repeated RETURNs or typing
`words`
will cause the counter to proceed, but it'll stop again on the input prompt.
This is because since version 3.9.6 the line input in EXPECT uses BASIN, i.e.
delegates the input wait to the CBM screen editor which only returns to
Forth code after RETURN is pressed, which means that until then PAUSE isn't
called and execution control isn't yielded to other tasks.
An alternative EXPECT is available in `x16input-tsk.fth` with the EXPECT
implementation of versions 3.9.5 and prior, similar to the C64/C16/Plus4
EXPECT, which implements the input loop in Forth, with a PAUSE call. On the
X16 this has the disadvantages that it uses a Kernal variable that could change
between Kernal versions, and has a bug that often leaves inverted spaces after
backspace is pressed. This being said,
```
include x16input-tsk.fth
```
after starting `taskdemo-x16.fth` will cause the counter to start running even
while waiting for input.
Typing `keyboard` will activate the original X16 EXPECT again and stop the
counter; typing `tasker-keyboard` will reactivate the task-switching EXPECT
and start the counter again.
## Implementation
As mentioned, the multitasker uses cooperative multitasking where
execution control is yielded via the word PAUSE. PAUSE saves the current state
of the currently active task and calls the task switcher. The state of a task
consists of the values of the Instruction Pointer (IP),
Return stack Pointer (RP) and Stack Pointer (SP).
The task switcher consists of a closed round-robin loop made up of the
first 6 bytes of the user area of each task.
See the picture [Tasks' user areas](#tasks-user-areas) in the
[Memory map](#memory-map) section.
Each task's user area starts with a machine code jump to
the next task's user area (`JMP XXXX` in the picture), followed by
`JSR wake`. `wake` is the task wake-up routine which sets UP to the task's
user area address calculated from return address left by the `jsr`,
restores SP from `user area + 6`and then restores RP and IP from the task's
data stack. The task's state is thereby restored, and the next iteration of
`NEXT` will call the task's next word, the one following the `PAUSE` that was
last invoked by the task.
A trick is employed to switch a task between active and inactive.
The JMP XXXX instruction at the user area's start, as described above, marks
an inactive task. When the round-robin loop reaches the task, the JMP
immediately forwards execution to the next task; the `JSR wake` is never
reached. For an active task, the JMP opcode is replaced by a BIT opcode (0x2c),
so that, when the task is jumped to, execution does reach the `JMP wake`
instruction, and the task runs until its reaaches the next `PAUSE`, which takes
the address of the BIT instruction for an indirect jump to the next task.
`SINGLETASK` changes `PAUSE` into a `NOOP` so that no task change at all
takes place when `PAUSE` is called. This is the default of a VolksForth
system without loaded multitasker. `MULTITASK` enables the task-switching
behaviour of `PAUSE`.
The system supports the multitasker by invoking `PAUSE` during many I/O
operations such as `KEY`, `TYPE` and `BLOCK`. In many situations this is
already sufficient for a task (e.g. the printer pooler) to run smoothly.
In other situations a suitable placement of `PAUSE` calls within foreground
or background task code may be useful.
Tasks are created in the dictionary of the foreground or console task. Each
task has its own user area with a copy of the user variables.
The implementation of the system is, however, simplified through the
restriction that only the console task can interpret or compile input text.
There is e.g. only one vocabulary search order across the system; if one task
changes the search order, this affects all other tasks, too. But this is not
really disturbing, since only the console task should use the search order
anyway.
Incidentally, it is possible to forget active tasks: `FORGET` removes all
tasks from the round-robin loop that are located in the dictionary range to
forget. This can still go wrong, though, if the forgotten task holds a
"Semaphor" (see below). Semaphores are not released during forgetting,
and the associated device will remain blocked.
Finally, it should be mentioned that when invoking a task name, the address
of the task's user area will be placed on the stack.
## Memory map
The memory used by VolksForth ranges from
`ORIGIN` to `LIMIT` . Below `ORIGIN` are the
Kernal variables, the screen memory and a single line of
BASIC with a `SYS` command that starts VolksForth.
Beyond `LIMIT` are I/O ports and the Kernal ROM.
Just below `LIMIT` the block buffers are stored; each with a size
of 1 Kbyte tall. When the system starts, as many buffers are
allocated as fit between `R0` and `LIMIT`.
The rest of the system, located between `ORIGIN` and `RO`, consists of
two areas. The upper area contains the return stack (growing downwards,
starting at `RO @`) and the user area (growing upwards, starting at `UP@`).
The dictionary and the data stack plus the heap occupy the other area.
Heap and data stack grow downwards, the heap from `UP@` and the data stack
from `S0 @`.
When the heap grows, the data stack is automatically moved downwards.
New words are stored in the dictionary, which is the fastest-growing
part of the system during compilation. Therefore the system automatically
checks that dictionary and data stack don't collide.
The Bootarea finally contains the initial values of the user
variables, which is copied during the cold start from there into the
console task's user area.
#### Overall memory map
```
0xFFFF -> ╔════════════════════╗
║ I/O and ROM ║
║ (starts at 0x8000, ║
║ 0x9F00, 0xD000 or ║
║ 0xFD00 depending ║
║ on platform) ║
limit -> ╠════════════════════╣
║ buffers ║
first @ -> ╠════════════════════╣
║ (unused) ║
r0 @ -> ╠════════════════════╣ ─────
║ return stack ║ ⋀
rp@ -> ╠═════════════════|══╣ |
║ free ║ rlen
║ ⋀ ║
╠═════════════════|══╣ |
║ user area ║
up@ -> ╠════════════════════╣ ─────
║ heap ║ ⋀
heap -> ╠════════════════════╣ |
║ (stack underflow) ║
s0 @ -> ╠════════════════════╣
║ stack ║
sp@ -> ╠═════════════════|══╣ slen
║ free ║
║ ⋀ ║
here -> ╠═════════════════|══╣
║ ║
║ dictionary ║
║ ║
╠════════════════════╣ |
║ boot area ║
origin -> ╠════════════════════╣ ─────
║ 10 SYS (2064) ║ SYS (4112) on the C16
╠════════════════════╣
║ video memory ║
╠════════════════════╣
║ Kernal vars ║
║ 6502 ZP, stack ║
╚════════════════════╝
```
#### 6502 zero page
The system also uses zero page memory, with the inner interpreter `NEXT`
and the stack pointers `RP` and `SP`, among others.
|Label | C64 | C16 | X16 |
|-------|-------|-------|-------|
|N |0x0029 |0x0029 |0x0067 |
|W |0x0021 |0x0021 |0x006F |
|IP |0x000E |0x000E |0x005C |
|NEXT |0x0009 |0x0009 |0x0057 |
|SP |0x0007 |0x0007 |0x0055 |
|Put A |0x0006 |0x0006 |0x0054 |
|UP |0x0004 |0x0004 |0x0052 |
|RP |0x0002 |0x0002 |0x0050 |
#### Memory map of a task
```
r0 @ -> ╔════════════════════╗ ─────
║ return stack ║ ⋀
rp@ -> ╠═════════════════|══╣ |
║ free ║ rlen
║ ⋀ ║
╠═════════════════|══╣ |
║ user area ║
up@ -> ╠════════════════════╣ ─────
║ heap ║ ⋀
heap -> ╠════════════════════╣ |
║ (stack underflow) ║
s0 @ -> ╠════════════════════╣
║ stack ║
sp@ -> ╠═════════════════|══╣ slen
║ free ║
║ ⋀ ║
here -> ╠═════════════════|══╣ |
║ dictionary ║
╚════════════════════╝ ─────
```
There's a small unused area of 6 bytes between stack and heap to prevent heap
corruption in case of a small stack underrun.
And typically, the dictionary of any task but the console task will be empty,
as that is where the outer interpreter is running which usally populates the
dictionary through definitions. However, `DP` is a user variable, so each task
has its own `HERE`, and if a task calls `ALLOT`, the memory will be allocated
in its own dictionary.
#### Tasks' user areas
A task is characterized by the address of its user area. The tasker's round
robin loop consists of
```
task 3: jmp XXXX => task is sleeping
│ ... │ task3 + 6
├───────────┤
│ jsr wake │ task3 + 3
├───────────┤
┌> -> -> │ jmp XXXX │-> ┐ task3 + 0
⋀ └───────────┘
| ┌ <- <- <-- <- <- <
| | task 2: bit XXXX => task is active
| | │ ... │ task2 + 6
├───────────┤
| | │ jsr wake │ task2 + 3
├───────────┤
| └> │ bit XXXX │-> ┐ task2 + 0
⋀ └───────────┘
| ┌ <- <- <-- <- <- <
| | task 1: bit XXXX => task is active
| | │ ... │ task1 + 6
├───────────┤
| | │ jsr wake │ task1 + 3
├───────────┤
| └> │ bit XXXX │-> ┐ task1 + 0
⋀ └───────────┘
<- <- <- <- <-- <- <- <
```
## Semaphores and LOCK
A problem that has not yet been mentioned: what happens when two tasks want to
print or access the disk drive simultaneously? Obviously, I/O devices may
at any time be used by only one task. This is achieved using Semaphores:
```
Create disp 0,
: newtype disp lock type disp unlock;
```
This has the following effect:
If two tasks call `NEWTYPE` at the same time, still only task at a time will
get to run `TYPE`, regardless of how many `PAUSE` are included in `TYPE`.
The phrase `DISP LOCK` will set up a barrier after the first task that performs
it, like switching a traffic light to red, and lets no other task pass.
The other task is held until the first task switches the traffic light to
green again via `DISP UNLOCK`. Then one (!) other task can pass the traffic
light, switching it red again, and so on.
By the way, the task that switched the traffic lights to red will not be
stopped by another `DISP LOCK` but will be let through. This is
necessary since `TYPE` could also contain a `DISP LOCK`.
(It doesn't in the above example, but it is conceivable.)
The implementation looks like this:
(Remember that every task is uniquely identifiable by the its user area
address.)
`DISP` is the so-called Semaphore; it must have the initial value 0.
`LOCK` looks at the Semaphore:
If it is zero, then the currently asctive task (i.e. its user area address)
is written to the semaphore, and the task may continue on its way.
If the value of the semaphore is the active task, then it may continue, too.
But if the value of the semaphore deviates from the active task's user area
address, then another task is active behind the traffic light, and the task
must `PAUSE` until the light turns green again, i.e. until the semaphore is
zero.
`UNLOCK` has nothing more to do than to set the value of the semaphore back to
zero.
`BLOCK` and `BUFFER` are, by the way, secured in this way for use by several
tasks:
only one task can request the loading of a block from the diskette at any given
time.
Whether `TYPE`, `EMIT`, etc. are also secured depends on their implementation -
`TYPE`, `EMIT`, etc are essentially deferred words with different possible
implementations.
## Remarks regarding BLOCK and a few other things
As can be seen from the glossary, only the address of the block buffer last
requested with `BLOCK` or `BUFFER` is valid, i.e. older blocks, depending on
the number of block buffers, may already be stored on the disk again. To be
on the safe side, it is useful to imagine that only one block buffer exists
in the entire system.
Now any task can run `BLOCK` and thus remove blocks from "under the feet"
of other tasks.
Therefore, one should not continue to use the address of a block after calling
a word that performs `PAUSE` performs. One should rather request the block
using `BLOCK` again. An example:
```
: .line ( block -- )
block c/l bounds DO I c@ emit LOOP ;
```
is WRONG because after `EMIT` the address range the address range that the
loop index steps through may no longer be correct.
```
: .line ( block -- )
c/1 0 DO dup block I + c@ emit LOOP drop ;
```
is CORRECT because it only keeps the number of the block, not the address of
its buffer.
```
: .line ( block -- ) block c/l type ;
```
is WRONG since `TYPE` may call `EMIT` repeatedly, and thus the address
delivered by `BLOCK` may become invalid during `TYPE`.
```
: >type ( adr len -- ) >r pad r@ cmove pad r> type ;
: .line -( block -—) block c/l >type ;
```
is CORRECT because `PAD` is different for each task.
In version 3.8 the word LIST was changed in such a way that it can be stopped
and terminated by pressing buttons, similar to `INDEX`. This entails certain
problems when this word is used in a task (e.g. a printer pooler). In this
case, both the console task and the tasks containing the word LIST attempt
to read inputs from the keyboard. If you type a text, the individual
characters are randomly distributed to the two tasks. Therefore, each task
(of course with the exception of the console task) should define its input
vector so that you cannot receive inputs.
The following code snippet contains an input vector that does not allow inputs.
See section Vectors in chapter Vectors and Deferred Words about defining
input or output vectors using `Input:` or `Output:`.
TODO: Add link once chapter Vectors and Deferred Words is translated.
```
: halt ." Task gestoppt!” stop ; \ completely stops the task
Input: nul-Input halt false drop halt ;
\ instead of: key key? decode expect
```
Another implementation of `KEY` for background tasks could be one that always
return a constant harmless character. Whether that works and with which
character must of course be decided for each application.
With `EXPECT` it is hard to envision a better solution, since `EXPECT` is
intended to change the `SPAN` variable which is a global variable, not a
per-task user variable.
One more note: A background task should not perform `ABORT"` or a similar word.
If it did, the task would become like the console task, resulting in a system
crash. Even if this possibility of error is intercepted and the task is
stopped, the problem remains that semaphores can still be "locked" on this task
and thus the use of certain parts of the forth remains blocked!
## Glossary
Multitasker words
#### `'s ( Tadr -- usradr )` "tick-s"
is used in the form:
```
<taskname> s <uname> ...
```
reads the name of a user variable `<uname>` and leaves the address `usradr` of
this user variable in the task marked by `Tadr`.
Tadr is usually generated by calling `<taskname>`. An error condition exists
if `<uname>` is not the name of a user variable.
See also `USER` and `TASK`. `S` is intended for changing the content of user
variables of a task by another task.
#### `activate ( Tadr -- )`
activates the task marked by `Tadr` and wakes it up. `Tadr` is usually
generated by calling a task name.
See also `SLEEP` , `STOP` , `PASS` , `PAUSE` , `UP@` , `UP` and `WAKE`.
#### `lock ( semadr -- )`
The semaphore with address `semadr` is blocked by the task that calls `LOCK`.
To do this, `LOCK` checks the contents of `semadr`. If the content indicates
that another task has blocked the semaphore, `PAUSE` will be executed until
the semaphore is released. When the semaphore is released, `LOCK` writes the
identifier of the task performing `LOCK` into the semaphore, thus blocking it
for all other tasks. So the code between `semadr LOCK` and `semadr UNLOCK`
can always be executed byonly one task at a time. See also `UNLOCK` and the
description of the [tasker](tasker.md#semaphores-and-lock).
#### `multitask ( -- )`
switches multitasking on. After `MULTITASK` has been executed, the word `PAUSE`
is no longer a `NOOP`. Instead, it yields control of the CPU to another task.
#### `pass (n0 ... nr-1 Tadr r -- )`
activates the task marked by `Tadr` and wakes it up. `Tadr` is usually
generated by calling a task name. `r` indicates the number of parameters
`nO` to `nr-1?` which are transferred from the stack of the task calling `PASS`
to the stack of the task identified by `Tadr`. The parameters `nO` to `nr-1`
are available to the task identified by `Tadr` for further use.
See also `STOP`, `ACTIVATE`, `PAUSE`, `UP@` and `UP!`.
#### `pause ( -- )`
a `NOOP`-word when single task mode is active. However, when multitasking is
activated, the task that performs `PAUSE` will yield control of the CPU to
another task. If there is only one task, or if all other tasks are asleep,
control will be returned to the task performin `PAUSE`. If at least one other
task is active, it will take overcontrol of the CPU, and will only yield it
again to another task when it calls `PAUSE` or `STOP`. Since the tasks are
linked to each other in a closed loop, the task that first performed `PAUSE`
will eventually gain control agian. An error condition exists if a task
never calls `PAUSE` or `STOP`. See also `STOP`, `MULTITASK` and `SINGLETASK`.
#### `rendezvous ( semadr -- )`
releases the semaphore with the address `semadr` and calls `PAUSE` in order to
allow other tasks to access the device which has been protected by this
semaphore. Then, `LOCK` is called in order to regain control of the device.
See als `LOCK` and `UNLOCK`.
#### `singletask ( -- )`
turns off multitasking. The word `PAUSE` is a `NOOP` word after `SINGLETASK`
has been run. An error condition exists if a background task calls `SINGLETASK`
without a subsequent `MULTITASK`, since in such a case the foreground or
console task would never regain control of the CPU.
See also `UP@` and `UP!`
#### `sleep ( Tadr -- )`
puts the task characterized by `Tadr` to sleep. `Tadr` is usually generated by
calling a task name. `SLEEP` has the same effect as the execution of `STOP`
by the task itself. The difference is that `STOP` is usually an end point in
processing. SLEEP will hit the task at an unforeseeable time so that the
ongoing work of the task is interrupted. See also `WAKE`.
#### `stop ( -- )`
causes the task calling `STOP` to sleep. The content of `IP` (instruction
pointer), `RP` (return stack pointer) and `SP` (stack pointer) are saved,
then control of the CPU is yielded to the next task. These actions are also
carried out by `PAUSE` (see there). The difference to `PAUSE` is that `STOP`
leaves the task inactive, while `PAUSE` leaves it active.
See also `ACTIVATE`, `PASS`, `WAKE`, `SLEEP`, `UP@` and `UP!`.
#### `Task ( rlen slen -- )`
is used in the form:
```
rlen slen Task <cccc>
```
`Task` is a defining word that sets up a task. A task is the work area for
another program that is to run simultaneously with the already running
programs. The task is named `cccc`, has a stack of size `slen` bytes and a
return stack of `rlen` bytes. The task's own dictionary (including `PAD`)
is located in the stack area, growing upwards while the stack grows downwards.
In the return stack area contains the task's user area (growing upwards)
and the return stack (growing downwards).
A task is thus a reduced image of the entire VolksForth system.
See the [task](tasker.md#memory-map-of-a-task) and
[system memory map](tasker.md#overall-memory-map).
Calling `cccc`, the task's name, as a word in any task leaves the same address
on the stack that the task `cccc` itself generates when calling `UP@`.
This address functions as the ID of the task and used by `S`, `ACTIVATE`,
`LOCK`, `PASS`, `SLEEP`, `TASK` and `WAKE`.
#### `tasks ( -- )`
lists the names of all existing tasks and shows whether they are
asleep or active.
#### `unlock ( semadr -- )`
releases the semaphore at address `semadr` for all tasks. If the semaphore is
owned by another task, then `UNLOCK` will wait, doing `PAUSE`, for the
until the semaphore is released. See also `LOCK` and the
description of the [tasker](tasker.md#semaphores-and-lock).
#### `up@ ( -- Tadr )` "u-p-fetch"
provides the address `Tadr` of the first byte of the user rarea of the
task calling `UP@`. `Tadr` is the address that characterizes each task,
the ID of the task.
See also `S` , `ACTIVATE`, `LOCK`, `PASS`, `SLEEP`, `TASK` and `WAKE`.
In the user area, variables and other data structures are stored of which
each task must have its own instance. See also `UP!`
#### `up! ( adr -- )` "u-p-store"
sets the `UP` (user pointer) to point to `adr`. See also `UP@`.
Note: This is potentially dangerous. Better know what you're doing when
using this.
#### `wake ( Tadr -- )`
wakes up the task identified by `Tadr`. `Tadr` is usually generated by
calling a task name. The specified task will continue to execute its code
where it was stopped by `SLEEP` - or where it ended itself by `STOP`,
thus be careful!.
See also `SLEEP`, `STOP`, `ACTIVATE` and `PASS`.

14
6502/C64/src/lists.fth Normal file
View File

@ -0,0 +1,14 @@
\ utility words for handling linked lists.
\ The first word of each list node contains the link to the next node.
: end-of-list ( list-node -- last-node )
BEGIN dup @ WHILE @ REPEAT ;
: reverse-list ( list-root-var -- )
dup >r @ 0 ( node[0] 0 )
BEGIN over WHILE ( node[i] node[i-1] )
over @ ( node[i] node[i-1] node[i+1] )
>r over ! r> ( node[i] node[i+1] )
swap REPEAT ( 0 node[n] )
r> ! drop ;

View File

@ -0,0 +1,25 @@
\ *** Block No. 58, Hexblock 3a
\ Multitasker BP 13.9.84 )
\needs Code include trns6502asm.fth
Code stop
SP 2dec IP lda SP X) sta
IP 1+ lda SP )Y sta
SP 2dec RP lda SP X) sta
RP 1+ lda SP )Y sta
6 # ldy SP lda UP )Y sta
iny SP 1+ lda UP )Y sta
1 # ldy tya clc UP adc W sta
txa UP 1+ adc W 1+ sta
W 1- jmp end-code
| Create taskpause Assembler
$2C # lda UP X) sta ' stop @ jmp
end-code
: singletask
[ ' pause @ ] Literal ['] pause ! ;
: multitask taskpause ['] pause ! ;

View File

@ -1,4 +1,3 @@
\ *** Block No. 2, Hexblock 2
\ rom ram sys cas16aug06
\ Shadow with Ctrl+W--->
@ -7,36 +6,23 @@
\ in the ROM Area
Assembler also definitions
(16 \ Switch Bank 8000-FFFF
\ Switch Bank 8000-FFFF
: rom here 9 + $8000 u> abort" not here"
$ff3e sta ;
: ram $ff3f sta ;
: sys rom jsr ram ;
\ if suffering from abort" not here"
\ see next screen Screen --> C)
\ see next screen Screen
(64 \ Switch Bank A000-BFFF
: rom here 9 + $A000 u> abort" not here"
$37 # lda 1 sta ;
: ram $36 # lda 1 sta ;
C)
\ *** Block No. 3, Hexblock 3
\ sysMacro Long cas16aug06
(64 .( not for C64 !) \\ C)
\ for advanced users, use macros
\ the following macro must be compiled well
\ below the address of $8000 to work.
here $8000 $20 - u> ?exit \ not possible
' 0 | Alias ???
Label long ROM

View File

@ -0,0 +1,14 @@
\ rom ram cas16aug06
\ macros switching the C64 BASIC ROM ($A000-$Bfff)
\ on and off. By default VolksForth runs with BASIC ROM
\ switched off.
Assembler also definitions
\ Can't swich on BASIC ROM (A000-BFFF) if current code
\ location is under the BASIC ROM.
: rom here 9 + $A000 u> abort" not here"
$37 # lda 1 sta ;
: ram $36 # lda 1 sta ;

View File

@ -0,0 +1,33 @@
\ Taskdemo for X16
: taskmark ; \needs cbm>scr : cbm>scr ;
\ This method of writing directly into the X16's screen memory
\ is a bit hacky and relies on the default settings that the
\ Kernal uses for text mode.
: scrstart ( -- adr) $b000 ;
: ctrl@ ( -- b ) $9f25 c@ ;
: addr-sel@ ( -- 0/1 ) ctrl@ 1 and ;
: datax! ( c -- ) $9f23 addr-sel@ + c! ;
: addrx-l/m! ( u -- ) $9f20 ! ;
: scr! ( scrcode vram-addr -- )
addrx-l/m! datax! ;
Variable counter counter off
$100 $100 Task Background
: >count ( n -)
Background 1 pass
counter !
BEGIN counter @ -1 counter +! ?dup
WHILE pause 0 <# #s #> dup >r
0 DO pause dup I + c@ cbm>scr
scrstart I 2* + scr! LOOP drop
bl r> 2* scrstart + scr!
REPEAT
BEGIN stop REPEAT ; \ stop's forever
: wait Background sleep ;
: go Background wake ;
multitask $1000 >count page

28
6502/C64/src/taskdemo.fth Normal file
View File

@ -0,0 +1,28 @@
\ Taskdemo clv12aug87
(CX cr .( this taskdemo works on c64 and c16 only) C)
(CX abort C)
: taskmark ; \needs cbm>scr : cbm>scr ;
: scrstart ( -- adr)
(64 $288 C) (16 $53e C) c@ $100 * ;
Variable counter counter off
$100 $100 Task Background
: >count ( n -)
Background 1 pass
counter !
BEGIN counter @ -1 counter +! ?dup
WHILE pause 0 <# #s #> dup >r
0 DO pause dup I + c@ cbm>scr
scrstart I + c! LOOP drop
bl r> scrstart + c!
REPEAT
BEGIN stop REPEAT ; \ stop's forever
: wait Background sleep ;
: go Background wake ;
multitask $100 >count page

92
6502/C64/src/tasker.fth Normal file
View File

@ -0,0 +1,92 @@
\ *** Block No. 57, Hexblock 39
\ Multitasker BP 13.9.84 )
Onlyforth
\needs multitask include multitask.fth save
\ *** Block No. 59, Hexblock 3b
\ pass activate ks 8 may 84 )
: pass ( n0 .. nr-1 Tadr r -- )
BEGIN [ rot ( Trick ! ) ]
swap $2C over c! \ awake Task
r> -rot \ IP r addr
8 + >r \ s0 of Task
r@ 2+ @ swap \ IP r0 r
2+ 2* \ bytes on Taskstack
\ incl. r0 & IP
r@ @ over - \ new SP
dup r> 2- ! \ into ssave
swap bounds ?DO I ! 2 +LOOP ;
restrict
: activate ( Tadr --)
0 [ -rot ( Trick ! ) ] REPEAT ;
-2 allot restrict
: sleep ( Tadr --)
$4C swap c! ; \ JMP-Opcode
: wake ( Tadr --)
$2C swap c! ; \ BIT-Opcode
\ *** Block No. 60, Hexblock 3c
\ building a Task BP 13.9.84 )
| : taskerror ( string -)
standardi/o singletask
." Task error : " count type
multitask stop ;
: Task ( rlen slen -- )
allot \ Stack
here $FF and $FE =
IF 1 allot THEN \ 6502-align
up@ here $100 cmove \ init user area
here $4C c, \ JMP opcode
\ to sleep Task
up@ 1+ @ ,
dup up@ 1+ ! \ link Task
3 allot \ allot JSR wake
dup 6 - dup , , \ ssave and s0
2dup + , \ here + rlen = r0
under + here - 2+ allot
['] taskerror over
[ ' errorhandler >body c@ ] Literal + !
Constant ;
\ *** Block No. 61, Hexblock 3d
\ more Tasks ks/bp 26apr85re)
: rendezvous ( semaphoradr -)
dup unlock pause lock ;
| : statesmart
state @ IF [compile] Literal THEN ;
: 's ( Tadr - adr.of.taskuservar)
' >body c@ + statesmart ; immediate
\ Syntax: 2 Demotask 's base !
\ makes Demotask working binary
: tasks ( -)
." MAIN " cr up@ dup 1+ @
BEGIN 2dup - WHILE
dup [ ' r0 >body c@ ] Literal + @
6 + name> >name .name
dup c@ $4C = IF ." sleeping" THEN cr
1+ @ REPEAT 2drop ;

View File

@ -0,0 +1,8 @@
cr .( tmpheap transient forth assembler) cr
here $800 tmp-hallot dp !
include 6502asm.fth
dp !

View File

@ -28,5 +28,5 @@ here dup origin!
$100 allot
Create logo
(C16+ ," volksFORTH-83 3.9.5-C16+ " )
(C16- ," volksFORTH-83 3.9.5-C16- " )
(C16+ ," volksFORTH-83 3.9.6-C16+ " )
(C16- ," volksFORTH-83 3.9.6-C16- " )

View File

@ -28,4 +28,4 @@ here dup origin!
$100 allot
Create logo
," volksFORTH-83 3.9.5-C64 "
," volksFORTH-83 3.9.6-C64 "

View File

@ -27,4 +27,4 @@ here dup origin!
$100 allot
Create logo
," volksFORTH-83 3.9.5-X16 "
," volksFORTH-83 3.9.6-X16 "

View File

@ -1,7 +1,4 @@
\ *** Block No. 126, Hexblock 7e
7e fthpage
\ CBM-Labels 05nov87re
$FFA5 >label ACPTR

View File

@ -8,13 +8,12 @@ include vf-lbls-cbm.fth
0ff4c >label ConOut
090 >label IOStatus
09a >label MsgFlg
0ae >label CurDev
0ff19 >label BrdCol
0ff15 >label BkgCol
053b >label PenCol
0cb >label CurFlg
0cf >label InsCnt
0cb >label QtSw
0cf >label Insrt
0540 >label KeyRep
055d >label PKeys
@ -51,6 +50,34 @@ Code curoff \ --
end-code
\ *** Block No. 131, Hexblock 83
83 fthpage
( #bs #cr ..keyboard clv12.4.87)
: c64key ( -- 8b)
curon BEGIN pause c64key? UNTIL
curoff getkey ;
14 Constant #bs 0D Constant #cr
: c64decode
( addr cnt1 key -- addr cnt2)
#bs case? IF dup IF del 1- THEN
exit THEN
#cr case? IF dup span ! exit THEN
>r 2dup + r@ swap c! r> emit 1+ ;
: c64expect ( addr len1 -- )
span ! 0
BEGIN dup span @ u<
WHILE key decode
REPEAT 2drop space ;
Input: keyboard [ here input ! ]
c64key c64key? c64decode c64expect ;
include vf-sys-cbm.fth

View File

@ -8,12 +8,11 @@ include vf-lbls-cbm.fth
0E716 >label ConOut
090 >label IOStatus
09d >label MsgFlg
0d020 >label BrdCol
0d021 >label BkgCol
0286 >label PenCol
0d4 >label CurFlg
0d8 >label InsCnt
0d4 >label QtSw
0d8 >label Insrt
028a >label KeyRep
0cc >label blnsw
@ -59,6 +58,34 @@ Code curoff ( --)
1 # ldy Next jmp end-code
\ *** Block No. 131, Hexblock 83
83 fthpage
( #bs #cr ..keyboard clv12.4.87)
: c64key ( -- 8b)
curon BEGIN pause c64key? UNTIL
curoff getkey ;
14 Constant #bs 0D Constant #cr
: c64decode
( addr cnt1 key -- addr cnt2)
#bs case? IF dup IF del 1- THEN
exit THEN
#cr case? IF dup span ! exit THEN
>r 2dup + r@ swap c! r> emit 1+ ;
: c64expect ( addr len1 -- )
span ! 0
BEGIN dup span @ u<
WHILE key decode
REPEAT 2drop space ;
Input: keyboard [ here input ! ]
c64key c64key? c64decode c64expect ;
include vf-sys-cbm.fth

View File

@ -1,32 +1,4 @@
\ *** Block No. 131, Hexblock 83
83 fthpage
( #bs #cr ..keyboard clv12.4.87)
: c64key ( -- 8b)
curon BEGIN pause c64key? UNTIL
curoff getkey ;
14 Constant #bs 0D Constant #cr
: c64decode
( addr cnt1 key -- addr cnt2)
#bs case? IF dup IF del 1- THEN
exit THEN
#cr case? IF dup span ! exit THEN
>r 2dup + r@ swap c! r> emit 1+ ;
: c64expect ( addr len1 -- )
span ! 0
BEGIN dup span @ u<
WHILE key decode
REPEAT 2drop space ;
Input: keyboard [ here input ! ]
c64key c64key? c64decode c64expect ;
\ *** Block No. 132, Hexblock 84
84 fthpage
@ -34,7 +6,15 @@ Input: keyboard [ here input ! ]
Code con! ( 8b --) SP X) lda
Label (con! ConOut jsr SP 2inc
Label (con!end CurFlg stx InsCnt stx
Label (con!end
\ So far VolksForth switches off quote switch and insert count
\ after every printed character. This introduces a dependency
\ on Kernal variables QtSw and Insrt that are undesirable on the
\ X16 where their addresses may change between Kernal versions.
\ Therefore we'll try how the system behaves without them on the
\ X16. Possibly this isn't needed at all, in the end.
(C64 QtSw stx Insrt stx )
(C16 QtSw stx Insrt stx )
1 # ldy ;c: pause ;
Label (printable? \ for CBM-Code !
@ -150,8 +130,10 @@ Label nodevice 0 # ldx 1 # ldy
\ ?device clv12jul87
Label (?dev
IOStatus stx \ because IOStatus isn't cleared by LISTEN or TALK
Label (?dev ( a: dev )
\ Clear IOStatus because it isn't cleared by LISTEN or TALK
(C64 IOStatus stx ( ) (C16 IOStatus stx ( )
(X16 pha 1 # lda ExtApi jsr pla ( )
\ It's unclear in which situation or use case the following
\ workaround for a C16 OS error is needed. The v4th tests pass
\ even with the following line removed.
@ -169,7 +151,7 @@ Label (?dev
i/o lock (?device ;
Code (busout ( dev 2nd -- )
MsgFlg stx 2 # lda Setup jsr
2 # lda Setup jsr
N 2+ lda (?dev jsr
N 2+ lda LISTEN jsr
N lda 60 # ora SECOND jsr
@ -192,7 +174,7 @@ Label (?dev
0E0 or busout busoff ;
Code (busin ( dev 2nd -- )
MsgFlg stx 2 # lda Setup jsr
2 # lda Setup jsr
N 2+ lda (?dev jsr
N 2+ lda TALK jsr
N lda 60 # ora (C16 $ad sta ( )

View File

@ -7,35 +7,22 @@ include vf-lbls-cbm.fth
0ffd2 >label ConOut
0febd >label KbdbufPeek
0287 >label IOStatus \ aka status
028d >label MsgFlg
0feab >label ExtApi
09f2c >label BrdCol
0376 >label BkgPenCol \ aka color
0381 >label CurFlg \ aka qtsw
0385 >label InsCnt \ aka insrt
\ I'm tentatively removing QtSw & Insrt from the X16 variant;
\ see comment at the top of vf-sys-cbm.fth
\ 0381 >label QtSw
\ 0385 >label Insrt
1 >label RomBank
0 >label RamBank
037B >label blnsw \ C64: $cc
\ 037C >label blnct \ C64: $cd
\ 037D >label gdbln \ C64: $ce
\ 037E >label blnon \ C64: $cf
\ 0262 >label pnt \ C64: $d1
\ 0380 >label pntr \ C64: $d3
\ 0373 >label gdcol
\ C64 labels that X16 doesn't have:
\ 028a >label KeyRep \ aka rptflg
\ *** Block No. 129, Hexblock 81
81 fthpage
\ X16 c64key? getkey
\ X16 x16key? getkey
Code c64key? ( -- flag)
Code x16key? ( -- flag)
KbdbufPeek jsr
txa pha
Push jmp end-code
@ -45,16 +32,33 @@ Code getkey ( -- 8b)
Push0A jmp end-code
\ *** Block No. 130, Hexblock 82
82 fthpage
\ *** Block No. 131, Hexblock 83
83 fthpage
\ X16 curon curoff
( #bs #cr ..keyboard clv12.4.87)
Code curon ( --)
blnsw stx Next jmp end-code
: x16key ( -- 8b)
BEGIN pause x16key? UNTIL getkey ;
Code curoff ( --)
blnsw sty Next jmp end-code
14 Constant #bs 0D Constant #cr
: x16decode
( addr cnt1 key -- addr cnt2)
#cr case? IF dup span ! exit THEN
>r 2dup + r> swap c! 1+ ;
Code basin ( -- 8b)
CHRIN jsr
Push0A jmp end-code
: x16expect ( addr len1 -- )
span ! 0
BEGIN dup span @ u<
WHILE basin x16decode
REPEAT 2drop space ;
Input: keyboard [ here input ! ]
x16key x16key? x16decode x16expect ;
include vf-sys-cbm.fth
@ -64,11 +68,9 @@ include vf-sys-cbm.fth
\ ... continued
8f fthpage
Create ink-pot
\ border bkgnd pen 0
6 c, 6 c, 3 c, 0 c, \ Forth
0E c, 6 c, 3 c, 0 c, \ Edi
6 c, 6 c, 3 c, 0 c, \ User
Create x16-ink-pot
\ border bkgnd-color-petscii pen-color-petscii
6 c, $1f c, $9f c, \ Forth
\ *** Block No. 144, Hexblock 90
@ -94,10 +96,10 @@ Label first-init
sei cld
RomBank lda $f8 # and RomBank sta \ map in KERNAL ROM
IOINIT jsr CINT jsr RESTOR jsr \ init. and set I/O-Vectors
ink-pot lda BrdCol sta \ border
ink-pot 1+ lda
.a asl .a asl .a asl .a asl \ backgrnd
ink-pot 2+ ora BkgPenCol sta \ pen
x16-ink-pot lda BrdCol sta \ border
x16-ink-pot 1+ lda ConOut jsr \ backgrnd
1 # lda ConOut jsr \ swap backgrnd <-> pen
x16-ink-pot 2+ lda ConOut jsr \ pen
$0e # lda ConOut jsr \ lower/uppercase
cli rts end-code
first-init dup bootsystem 1+ !

View File

@ -0,0 +1,35 @@
\ Sample: a changed input that would run the tasker
\ even while waiting for keyboard input, similar to
\ the keyboard input on C16 and C64:
\needs Code include trns6502asm.fth
\ X16 labels
$037B >label blnsw \ C64: $cc
\ X16 curon curoff
Code curon ( --)
blnsw stx Next jmp end-code
Code curoff ( --)
blnsw sty Next jmp end-code
: c64decode
( addr cnt1 key -- addr cnt2)
#bs case? IF dup IF del 1- THEN
exit THEN
#cr case? IF dup span ! exit THEN
>r 2dup + r@ swap c! r> emit 1+ ;
: c64expect ( addr len1 -- )
span ! 0
BEGIN dup span @ u<
WHILE curon key curoff decode
REPEAT 2drop space ;
Input: tasker-keyboard [ here input ! ]
x16key x16key? c64decode c64expect ;
tasker-keyboard

View File

@ -5,7 +5,7 @@
\ tmpclear will remove all words on the tmpheap, wheras regular clear
\ will remove all words on tmpheap and heap together.
\ Other than the reference tmpheap living on the regular heap, this
\ In contrast to the reference tmpheap living on the regular heap, this
\ custom tmpheap needs no initialization as its position and
\ size (8k) is fixed.

View File

@ -21,8 +21,8 @@ YOU SHOULD SEE THE NUMBER RANGES OF SIGNED AND UNSIGNED NUMBERS:
UNSIGNED: 0 FFFF
*
PLEASE TYPE UP TO 80 CHARACTERS:
1234567890
RECEIVED: "1234567890"
TMPL_KEYS_ECHO_TMPL
RECEIVED: "TMPL_KEYS_RECEIVED_TMPL"
* GDX exists
End of Core word set tests

View File

@ -2,6 +2,25 @@
* C64/C16/Plus4/CommanderX16
See also [[6502/C64/RELEASE_NOTES.md]]
*3.9.6*
* Removes all known dependencies of the X16 VolksForth kernel on
undocumented X16 Kernal variables and thus (hopefully) on specific
X16 ROM versions. This has some implications on multitasking behaviour
while waiting for console input.
* Adds more bundled Forth sources from the original disks.
* Starts an English translation of the original German manual with a
translation of the chapter on the multitasker.
*3.9.5*
* X16 version adapted to the X16 ROM version R46. It also runs with
the R47 ROM.
* Adds a second X16 binary with a word (XED) use the ROM-based editor
X16Edit, and with some DOS and file support (DIR, CAT, DOS).
*3.9.4*
* X16 version adapted to Kernal/Emulator version R41