whartung wrote:
You're not content with some kind of semaphore system protecting the resource? (Printer, screen, etc.)
I haven't added semaphores to the multitasker yet, but my understanding of their use is if you have more than one task ( or computer) and a single shared resource ( such as a printer), each task gets exclusive use until it finishes on a first access, first serve basis. In my system, the programmer ( the main or foreground task) has full use of the screen and printer.
TYPE and
EMIT use the Commodore 64 kernel routines for output. Here are the I/O redirection words as they were
without multitasker support.
Code:
SCR# 2E
// OUTPUT REDIRECTION
HEX
// PRINTER DEVICE NUMBER
4 VALUE #LP
// PRINTER SECONDARY ADDRESS
0 VALUE #LP2
: (PEMIT) ( C -- )
#LP (CHKOUT) IOERR
(EMIT) CLRCHN ;
: (PTYPE) ( ADR U -- )
#LP (CHKOUT) IOERR (TYPE) ;
SCR# 2F
// <PQTYPE>
HEX
: <PQTYPE> ( ADR CNT -- )
#LP (CHKOUT) IOERR
>ASSEM
2 # LDA, SETUP JSR,
BEGIN,
BEGIN,
N CPY,
0= IF,
N 1+ LDA,
0= IF,
' CLRCHN @ JMP,
THEN,
N 1+ DEC,
THEN,
SCR# 30
// <PQTYPE> (PQTYPE)
N 2+ )Y LDA,
BL # CMP, CS IF,
7B # CMP, CS IF,
0C1 # CMP, CS IF,
0DB # CMP, CS ELIF,
CS-ROT THEN,
5F # LDA,
THEN,
THEN,
' (EMIT) @ 8 + JSR, INY,
0= UNTIL,
N 3 + INC,
AGAIN, END-CODE
: (PQTYPE) ( ADR CNT -- )
<PQTYPE> PAUSE ;
SCR# 31
// LOGGER REDIRECTION
HEX
: (LEMIT) ( B -- )
DUP DEMIT (PEMIT) ;
: (LTYPE) ( ADR CNT -- )
2DUP DTYPE (PTYPE) ;
: (LQTYPE) ( ADR CNT -- )
2DUP #OUT @ -ROT <PQTYPE> #OUT !
(QTYPE) ;
: (LEXPECT) ( ADR CNT -- )
OVER SWAP (EXPECT)
#LP (CHKOUT) IOERR
SPAN @ DTYPE BL DEMIT CLRCHN ;
SCR# 32
// CONSOLE PRINTER
: CONSOLE ( -- )
#LP CLOSE CLRCHN
['] (EMIT) IS EMIT
['] (TYPE) IS TYPE
['] (QTYPE) IS QTYPE
['] (EXPECT) IS EXPECT ;
: PRINTER ( -- )
#LP CLOSE
0 0 #LP DUP #LP2 (OPEN) IOERR
['] (PEMIT) IS EMIT
['] (PTYPE) IS TYPE
['] (PQTYPE) IS QTYPE
['] (EXPECT) IS EXPECT ;
SCR# 33
// LOGGER
: LOGGER ( -- )
PRINTER
['] (LEMIT) IS EMIT
['] (LTYPE) IS TYPE
['] (LQTYPE) IS QTYPE
['] (LEXPECT) IS EXPECT ;
CONSOLE
There are two variables to help with output formatting,
#OUT and
#LINE.
PAGE turns both off.
CR ( or 13 ( $D )
EMIT ) turns
#OUT off and increments
#LINE. Outputting other characters increments
#OUT.
(TYPE) JSR's into the main part of
(EMIT) to maintain these variables.
DEMIT and
DTYPE are versions of
(EMIT) and
(TYPE) that do
not affect the formatting variables. They are for communicating with a device such as a disk drive.
QTYPE ( quote type) is for those occasions when listing a
BLOCK or getting an
INDEX ( showing the first line of a range of blocks ) and the block(s) in question may hold data rather than source code.
QTYPE displays the control characters as reverse video characters ( or an underscore on the printer ) rather than allowing the control characters to affect the display.
(TYPE) and
(QTYPE) call
CLRCHN , to clear the channel, and
PAUSE.
#OUT must only be incremented
once for each character displayed ( or printed) and
#LINE must only be incremented once for each carriage return sent or the output from something like
WORDS would look rather strange.
Keep in mind that this I/O redirection worked great before the introduction of multitasking ( and works well with tasks that don't need to display anything.)
The programmer has typed
LOGGER and pressed the enter key.
TYPE and
EMIT ( and couple of others ) are redirected to send output to both the screen and the printer ( the programmer wants a record of his ( or her) interactive Forth session). Now lets introduce multitasking. Here is a background task to print the time at the top right corner of the display. There are no fancy windows, this is a Commodore 64. Never mind that if I wanted the time displayed, I would write it directly to the screen with the task updating a small buffer with the correct screen data every minute, but copying that buffer to the correct screen address each time it ran ( or the time display would scroll away whenever the main task caused the screen to scroll.) In this example, the task uses
TYPE to display the time. It uses
XY and
AT-XY to save, manipulate, and restore the cursor position. the problem is that each time the time display task updates the time display, a copy of the time is sent to the printer as well. If there is a semaphore to block access to the printer if another task is using it ( like the main task) then wouldn't this task just sit there and
PAUSE each time it ran? If that is the case, then the time would never get displayed on the screen either. This defeats the purpose of having a time display task.
Whartung, If you have an elegant solution to this, I'd like to see it. Without code or diagrams I'm having a difficult time wrapping my mind around using semaphores to handle this.