rewrite vt100 and telnet.

This commit is contained in:
Kelvin Sherlock 2018-04-21 15:30:26 -04:00
parent 9285503c5f
commit 9cc53483ad
10 changed files with 3395 additions and 3359 deletions

32
GNUmakefile Normal file
View File

@ -0,0 +1,32 @@
PROG = fctelnet
OBJS = o/fctelnet.a o/vt100.a o/telnet.a o/ansi.a o/chars.a o/marinetti.a o/display.a
OPTIMIZE ?= 79
CFLAGS = -w-1 -O $(OPTIMIZE)
CC = occ --gno
$(PROG): $(OBJS)
$(RM) o/ansi.root
$(RM) o/chars.root
$(CC) $(OBJS) -o $@
fctelnet.o: fctelnet.c marinetti.h
vt100.o: vt100.c CLAGS+=-r
ansi.o: ansi.asm
chars.o: chars.asm
marinetti.o: marinetti.c CLAGS+=-r
telnet.o: telnet.c CLAGS+=-r
o/%.a : %.c
$(CC) -c $(CFLAGS) -o $@ $^
o/%.a : %.asm
$(CC) -c $(CFLAGS) -o $@ $^
clean:
$(RM) -f *.o *.root *.a *.r
clobber: clean
$(RM) -f $(PROG)

661
ansi.asm
View File

@ -3,6 +3,46 @@
dummy start
end
tables privdata
;
; each line is 160 pixels.
; top/bottom margin is 4 lines (4*160 pixels)
rows anop
dc i2'0*8*160+4*160+$2000'
dc i2'1*8*160+4*160+$2000'
dc i2'2*8*160+4*160+$2000'
dc i2'3*8*160+4*160+$2000'
dc i2'4*8*160+4*160+$2000'
dc i2'5*8*160+4*160+$2000'
dc i2'6*8*160+4*160+$2000'
dc i2'7*8*160+4*160+$2000'
dc i2'8*8*160+4*160+$2000'
dc i2'9*8*160+4*160+$2000'
dc i2'10*8*160+4*160+$2000'
dc i2'11*8*160+4*160+$2000'
dc i2'12*8*160+4*160+$2000'
dc i2'13*8*160+4*160+$2000'
dc i2'14*8*160+4*160+$2000'
dc i2'15*8*160+4*160+$2000'
dc i2'16*8*160+4*160+$2000'
dc i2'17*8*160+4*160+$2000'
dc i2'18*8*160+4*160+$2000'
dc i2'19*8*160+4*160+$2000'
dc i2'20*8*160+4*160+$2000'
dc i2'21*8*160+4*160+$2000'
dc i2'22*8*160+4*160+$2000'
dc i2'23*8*160+4*160+$2000'
dc i2'24*8*160+4*160+$2000'
;
; columns are simply x * 2
;
end
;
;ReverseScrollRegion(line1, line2)
;
@ -12,6 +52,8 @@ line1 equ 7
_rtlb equ 3
_d equ 1
using tables
; brk $ea
phb
@ -20,36 +62,28 @@ _d equ 1
tcd
lda <line1
cmp #0
bcc exit
bmi exit
lda <line2
cmp #24
cmp #25
bcs exit
lda <line1
cmp <line2
bcs exit
; a = (line2 - line1) * 8 * 160 - 1
asl line1
asl line2
lda <line2
sec
sbc <line1
; a = a * 256 * 5
xba ; a = a * 256
; 5x = 4x + x
; count = (line2 - line1) * 8 * 160 - 1
pha ; save
asl a ; x2
asl a ; x4
clc
adc 1,s
sta 1,s
pla
dec a ; a = a - 1
ldx <line2
lda >rows,x
clc ; dec a
ldx <line1
sbc >rows,x
pha
pha ; save
;
; mvn:
@ -66,21 +100,14 @@ _d equ 1
; x = src address = dest - 160*8
lda <line2
inc a
xba ; x256
pha ; save
asl a ; x 2
asl a ; x 4
clc
adc #$2000+4*160
adc 1,s
dec a ; - 1
sta 1,s ;store it...
ldx <line2
lda >rows,x
dec a
tay
sec
sbc #1280
tax ; src address
ply ; dest address
sbc #160*8
tax
pla ; length
mvp $e10000,$e10000
@ -88,8 +115,8 @@ _d equ 1
;
; now empty first line
;
pei (line1)
jsl ClearLine
ldx <line1
jsr _ClearLine
exit anop
@ -113,6 +140,8 @@ exit anop
;
ScrollRegion START
using tables
line2 equ 9
line1 equ 7
_rtlb equ 3
@ -126,36 +155,28 @@ _d equ 1
tcd
lda <line1
cmp #0
bcc exit
bmi exit
lda <line2
cmp #24
cmp #25
bcs exit
lda <line1
cmp <line2
bcs exit
asl line1
asl line2
; a = (line2 - line1) * 8 * 160 - 1
lda <line2
sec
sbc <line1
; a = a * 256 * 5
xba ; a = a * 256
; 5x = 4x + x
ldx <line2
lda >rows,x
clc ; dec a
ldx <line1
sbc >rows,x
pha
pha ; save
asl a ; x2
asl a ; x4
clc
adc 1,s
sta 1,s
pla
dec a ; a = a - 1
pha ; save
; src address = $2000 + line1
@ -166,18 +187,13 @@ _d equ 1
; dest = $2000 + 640 + line1 * 8 * 160
; src = dest + 1280
lda <line1
xba ; x256
pha ; save
asl a ; x 2
asl a ; x 4
ldx <line1
lda >rows,x
tay
clc
adc #$2000+4*160
adc 1,s
sta 1,s ;store it...
adc #1280
tax ; src address
ply ; dest address
adc #8*160
tax
pla ; length
mvn $e10000,$e10000
@ -185,8 +201,10 @@ _d equ 1
;
; now empty last line
;
pei (line2)
jsl ClearLine
ldx line2
dex
dex
jsr _ClearLine
exit anop
@ -210,6 +228,8 @@ exit anop
;
ScrollDown START
using tables
phb
;
; mvn:
@ -232,8 +252,8 @@ ScrollDown START
;
; now empty outline #24
;
pea 23
jsl ClearLine
ldx #23
jsr _ClearLine
plb
@ -241,54 +261,50 @@ ScrollDown START
END
; clear line in X (which has been pre-multiplied)
_ClearLine PRIVATE
using tables
lda >rows,x
tax
lda #0
sta >$e10000,x ; stick a 0 in there
txy ; destination address (src + 2)
iny
iny
lda #8*160-3 ; length -1
mvn $e10000,$e10000
rts
END
;
; ClearLine(int line_no); clears the line
;
ClearLine START
line equ 9
_rtlb equ 5
tmp equ 3
line equ 7
_rtlb equ 3
_d equ 1
; brk $ea
phb
pha
phd
tsc
tcd
; line * 8 * 160 + 4*160
; = line * 1024 + line * 256 + 4*160
lda <line
cmp #24
bcs exit
and #$00ff
xba ; x256
sta <tmp
asl a ; x 512
asl a ; x 1024
clc
adc <tmp
sta <tmp
clc
adc #4*160
sta <tmp
clc
adc #$2000 ; graphics begin @ $e12000
asl a
tax
lda #0
sta >$e10000,x ; stick a 0 in there
txy ; destination address (src + 2)
iny
iny
jsr _ClearLine
lda #8*160-3 ; length -1
mvn $e10000,$e10000
exit anop
lda <_rtlb+2
sta <_rtlb+4
@ -297,74 +313,76 @@ _d equ 1
pld
pla
pla
plb
rtl
END
;extern void ClearScreenFrom(int Y);
;
; ClearScreen2(int start, int end);
;
;
ClearScreenFrom START
ClearScreen2 START
Y equ 9
_rtlb equ 5
tmp equ 3
using tables
line2 equ 9
line1 equ 7
_rtlb equ 3
_d equ 1
; brk $ea
phb
pha
phd
tsc
tcd
; line * 8 * 160 + 4*160
; = line * 1024 + line * 256 + 4*160
lda <Y
lda <line1
bmi exit
and #$00ff
xba ; x256
sta <tmp
asl a ; x 512
asl a ; x 1024
clc
adc <tmp
sta <tmp
lda line2
cmp #25
bcs exit
clc
adc #4*160
sta <tmp
clc
adc #$2000 ; graphics begin @ $e12000
lda <line1
cmp <line2
bcs exit
asl line1
asl line2
ldx <line2
lda >rows,x
clc
ldx <line1
sbc >rows,x
dec a
dec a
pha ; save length
ldx <line2
lda >rows,x
tax
tay
iny
iny
lda #0
sta >$e10000,x ; stick a 0 in there
txy ; destination address (src + 2)
iny
iny
phy
lda #$9d00-2 ; end of graphics + $2000
sec
sbc 1,s
ply
; a now equals length to clear
sta >$e1000,x
pla ; restore length
mvn $e10000,$e10000
exit anop
lda <_rtlb+2
sta <_rtlb+4
sta <line2
lda <_rtlb
sta <_rtlb+2
sta <line1
pld
pla
@ -375,22 +393,23 @@ _d equ 1
END
;
;extern void ClearLineFrom(int Y, int X);
; ClearLine2(int line, int start, int end);
;
;
; clear the end of the line from a given x & y
; clear a section of a given line.
;
ClearLineFrom START
ClearLine2 START
X equ 11
Y equ 9
_rtlb equ 5
tmp equ 3
using tables
end equ 11
start equ 9
line equ 7
_rtlb equ 3
_d equ 1
phb
pha
phd
tsc
tcd
@ -400,62 +419,62 @@ _d equ 1
plb
;
; if X is >= 80, skip it...
;
lda <X
cmp #80
bcs exit
lda <Y
and #$00ff
xba ; x 256
sta <tmp
asl a ; x 512
asl a ; x 1024
clc
adc <tmp ; Y * 8 * 160
clc
adc #160*4
sta <tmp
; each char takes up 2 bytes
lda <X
asl a ; x 2
clc
adc <tmp
sta <tmp
tax
lda #80
sec
sbc <X
loop anop
dec a
lda start
bmi exit
stz $2000,x
stz $2000+160,x
stz $2000+320,x
stz $2000+480,x
stz $2000+640,x
stz $2000+800,x
stz $2000+960,x
stz $2000+1120,x
lda end
cmp #81
bcs exit
lda start
cmp end
bcs exit
sec
lda end
sbc start
; bmi exit
; beq exit
tay ; counter
asl start ; x 2
asl line
ldx <line
lda >rows,x
clc
adc start
tax
loop anop
stz |0,x
stz |160,x
stz |320,x
stz |480,x
stz |640,x
stz |800,x
stz |960,x
stz |1120,x
inx
inx
bra loop
dey
bne loop
exit anop
lda <_rtlb+2
sta <end
lda <_rtlb
sta <start
pld
pla
pla
sta 3,s
pla
sta 3,s
plb
rtl
END
@ -533,242 +552,214 @@ ClearScreen START
plb
plb
lda #0
ldy #0
ldy #32000
loop anop
sta $2000,y
iny
iny
cpy #32000
bcc loop
sta |$2000-2,y
dey
dey
bne loop
plb
rtl
;
; this will, in 1 atomic action, blast the screen with 0
;
phb
lda #0
sta >$e12000
lda #32000-3 ;length
ldx #$2000
ldy #$2002
mvn $e10000,$e10000
plb
rtl
END
;
; draw a character at the current Xpos & Ypos
;
;
; PrintChar(x, y, char, andMask, xorMask)
PrintChar START
andMask equ 11
char equ 9
_rtlb equ 5
pos equ 3
using tables
xorMask equ 15
andMask equ 13
char equ 11
yy equ 9
xx equ 7
_rtlb equ 3
_d equ 1
; brk $ea
phb
pha
phd
tsc
tcd
; sanity check on Xpos && Ypos
lda Xpos
cmp #80
bcc _ok
lda Ypos
cmp #24
bcc _ok
brl exit
_ok anop
ldx xx
cpx #80
bcc ok2
exit2 anop
brl exit
ok2 anop
ldy yy
cpy #24
bcs exit2
lda <char
cmp #$7f
bcs space
sec
sbc #$20
bmi space
asl a ; x2
asl a ; x4
asl a ; x8
asl a ; x16
sta char
bra ok
space anop
stz <char
ok anop
pea $e1e1
plb
plb
;
; each line has a width of 160 pixels
;
; start drawing the letter at $e12000 + (Ypos x 160 x 8) + Xpos
asl xx
asl yy
; 160 * 8 = 1240 = 1024 + 256
lda >Ypos
and #$00ff
xba ; x 256
sta <pos ; store temporarily
asl a ; x 512
asl a ; x 1024
ldx yy
lda >rows,x
clc
adc <pos
sta <pos
;
; lower 1/2 row additional
;
clc
adc #160*4
sta <pos
;
; for x offset, 160 bytes per row / 80 chars = 2 bytes/letter
;
lda >Xpos
asl a ; x 2
clc
adc <pos
sta <pos
adc xx
tay
;
; $e12000 + pos = location to start drawing the character
;
lda <char
cmp #$20 ; smallest printing char
bcc exit ; not printable....
cmp #127 ; 126 is biggest
bcs exit ; not printable
;
; each char takes up 16 bytes :. the offset is (char - $20) * 16
;
sec
sbc #$20
asl a ; x2
asl a ; x4
asl a ; x8
asl a ; x16
tax
ldx char
lda >CHAR_SPACE,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000,y ; blast it to the screen
sta |0,y ; blast it to the screen
lda >CHAR_SPACE+2,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+160,y
sta |160,y
lda >CHAR_SPACE+4,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+320,y
sta |320,y
lda >CHAR_SPACE+6,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+480,y
sta |480,y
lda >CHAR_SPACE+8,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+640,y
sta |640,y
lda >CHAR_SPACE+10,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+800,y
sta |800,y
lda >CHAR_SPACE+12,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+960,y
sta |960,y
lda >CHAR_SPACE+14,x
eor >xor_mask
eor <xorMask
and <andMask
sta |$2000+1120,y
sta |1120,y
exit anop
lda <_rtlb+2
sta <andMask
sta <xorMask
lda <_rtlb
sta <char
sta <andMask
pld
pla
pla
pla
pla
pla
plb
rtl
end
Cursor START
;Cursor START
StartCursor ENTRY
;StartCursor ENTRY
pha
; ~ReadBParam #$2f
pla
sta blinkRate
; pha
;; ~ReadBParam #$2f
; pla
; sta blinkRate
stz savedX
stz savedY
; stz savedX
; stz savedY
; ~SetHeartBeat #CursorHB
rtl
;; ~SetHeartBeat #CursorHB
; rtl
StopCursor ENTRY
; ~DelHeartBeat #CursorHB
rtl
;StopCursor ENTRY
;; ~DelHeartBeat #CursorHB
; rtl
notSafe ENTRY
ds 2
blinkRate ds 2
savedX ds 2
savedY ds 2
;notSafe ENTRY
; ds 2
;blinkRate ds 2
;savedX ds 2
;savedY ds 2
CursorHB anop
dc i4'0'
count dc i2'30'
dc h'5AA5'
;CursorHB anop
; dc i4'0'
;count dc i2'30'
; dc h'5AA5'
longa off
; longa off
phb
phk
plb
php
; phb
; phk
; plb
; php
; long ai
lda notSafe
bne exit
;; long ai
; lda notSafe
; bne exit
lda Xpos
cmp savedX
bne moved
lda Ypos
cmp savedY
bne moved
bra exit
; lda Xpos
; cmp savedX
; bne moved
; lda Ypos
; cmp savedY
; bne moved
; bra exit
moved jsr drawCursor
;moved jsr drawCursor
exit anop
;exit anop
plp
plb
clc
rtl
; plp
; plb
; clc
; rtl
drawCursor anop
;drawCursor anop
rts
; rts
END
; END

384
display.c
View File

@ -1,54 +1,21 @@
#pragma noroot
#pragma lint -1
#include <types.h>
#include <string.h>
#include <ctype.h>
#include <Event.h>
#include "Marinetti.h"
#define ESC "\x1b"
extern void ScrollRegion(word,word);
extern void ScrollDown(void);
extern void PrintChar(int the_char, unsigned int andMask);
extern void ClearScreen(void);
extern void ClearScreenFrom(int Y);
extern void ClearLine(int Y);
extern void ClearLineFrom(int Y, int X);
void handle_ansi(word code, char *buffer, word cnt);
extern void GrafOn(void) inline(0x0a04, 0xe10000);
extern void GrafOff(void) inline(0x0b04, 0xe10000);
extern pascal void SetColorTable(Word, ColorTable) inline(0x0E04,dispatcher);
extern pascal void SysBeep2(Word) inline(0x3803,dispatcher);
extern pascal void SetAllSCBs(Word) inline(0x1404,dispatcher);
extern pascal void SetMasterSCB(Word) inline(0x1604,dispatcher);
extern pascal void InitColorTable(ColorTable) inline(0x0D04,dispatcher);
extern word Xpos;
extern word Ypos;
extern word __scroll[2];
extern word and_mask;
extern word xor_mask;
word esc;
char esc_buffer[256];
struct errlist
{
struct errlist {
Word Error;
Word MsgLen;
const char *Msg;
};
#define _ERR(n,msg) { n, sizeof(msg), msg }
#define ESC "\x1b"
#define _ERR(n,msg) { n, sizeof(msg) - 1, msg }
struct errlist errors[] =
{
extern void vt100_process(const unsigned char *buffer, unsigned buffer_size);
static struct errlist errors[] = {
_ERR(1,"tcperrDeafDestPort"),
_ERR(2,"tcperrHostReset"),
_ERR(3,"tcperrConExists"),
@ -63,346 +30,27 @@ struct errlist errors[] =
_ERR(0x0c,"tcperrConReset"),
_ERR(0x0d,"tcperrUserTimeout"),
_ERR(0x0e,"tcperrConRefused"),
{0,0,NULL}
};
void display_str(const char *buffer, word cnt);
void display_err(Word err)
{
struct errlist *l;
void display_err(Word err) {
for (l = &errors[0]; l->Error; l++)
if (l->Error == err) break;
if (l->Error != err) return;
display_str("\r\n" "\x1b" "[1m", 6); // bold on
display_str(l->Msg, l->MsgLen); // show the string
display_str("\x1b" "[0m", 4); // bold off
if (err == 0 || err >= 0x0f) return;
--err;
vt100_process("\r\n" ESC "[1m", 6); // bold on
vt100_process(errors[err].Msg, errors[err].MsgLen);
vt100_process(ESC "[0m", 4); // bold off
}
void display_cstr(const char *cstr)
{
void display_cstr(const char *cstr) {
if (cstr && *cstr)
display_str(cstr, strlen(cstr));
vt100_process(cstr, strlen(cstr));
}
void display_pstr(const char *pstr)
{
void display_pstr(const char *pstr) {
if (pstr && *pstr)
display_str(pstr + 1, *pstr);
vt100_process(pstr + 1, *pstr);
}
void display_char(char c)
{
switch(c)
{
case 0x07:
SysBeep2(0);
break;
//backspace
case 0x08:
if (Xpos) Xpos--;
break;
//tab
case 0x09:
// 1, 9, 17, 25, ...
// not sure this is right
Xpos = Xpos & 0xf8;
Xpos += 8;
//TODO - wrapping
break;
// line feed
case 0x0A:
if (++Ypos > 23)
{
ScrollRegion(__scroll[0], __scroll[1]);
Ypos = 23;
}
break;
case 0x0D: //carriage return
Xpos = 0;
break;
default:
if (isprint(c));
{
// check the wrapping flag ...
if (Xpos > 80)
{
Xpos = 0;
if (++Ypos > 23)
{
ScrollRegion(__scroll[0],
__scroll[1]);
Ypos = 23;
}
}
PrintChar(c, and_mask);
Xpos++;
}
}
}
/*
* this reads text & has it drawn on the shr screen
*
*/
// todo - and_mask + xor_mask
void display_str(const char *buffer, word cnt)
{
word i;
asm
{
phk
plb
}
for ( i = 0; i < cnt; i++)
{
if (esc)
{
esc_buffer[esc++] = buffer[i];
if (isalpha(buffer[i])
|| buffer[i] == '~'
|| buffer[i] == '='
|| buffer[i] == '>'
)
{
handle_ansi(buffer[i],
esc_buffer, esc);
esc = 0;
}
}
else switch (buffer[i])
{
// bell
case 0x07:
SysBeep2(0);
break;
//backspace
case 0x08:
if (Xpos) Xpos--;
break;
//tab
case 0x09:
// 1, 9, 17, 25, ...
// not sure this is right
Xpos = Xpos & 0xf8;
Xpos += 8;
//TODO - wrapping
break;
// line feed
case 0x0A:
if (++Ypos > 23)
{
ScrollRegion(__scroll[0],
__scroll[1]);
Ypos = 23;
}
break;
case 0x0D: //carriage return
Xpos = 0;
break;
case 0x1b: //start of escape sequence
esc = 1;
esc_buffer[0] = 0x1b;
break;
default:
if (isprint(buffer[i]))
{
// check the wrapping flag ...
if (Xpos > 80)
{
Xpos = 0;
if (++Ypos > 23)
{
ScrollRegion(__scroll[0],
__scroll[1]);
Ypos = 23;
}
}
PrintChar(buffer[i], and_mask);
Xpos++;
}
}
}
}
//
// remap the iigs key to a vt100 code (if necessary) and send it out.
void send_event(EventRecord *event, Word ipid)
{
char *cp;
int len = 0;
word key;
word mods;
key = event->message;
mods = event->modifiers;
if (mods & keyPad)
{
switch (key)
{
case 0x72: // insert
cp = ESC "[2~";
len = 4;
break;
case 0x75: // delete
cp = ESC "[3~";
len = 4;
break;
case 0x73: // home
cp = ESC "[1~";
len = 4;
break;
case 0x77: // end
cp = ESC "[4~";
len = 4;
break;
case 0x74: // page up
cp = ESC "[5~";
len = 4;
break;
case 0x79: // page down
cp = ESC "[6~";
len = 4;
break;
case 0x7a: // f1
cp = mods & shiftKey ? ESC "[23~" : ESC "[11~";
len = 5;
break;
case 0x78: // f2
cp = mods & shiftKey ? ESC "[24~" : ESC "[12~";
len = 5;
break;
case 0x63: // f3
cp = mods & shiftKey ? ESC "[25~" : ESC "[13~";
len = 5;
break;
case 0x76: // f4
cp = mods & shiftKey ? ESC "[26~" : ESC "[14~";
len = 5;
break;
case 0x60: // f5
cp = mods & shiftKey ? ESC "[28~" : ESC "[15~";
len = 5;
break;
case 0x61: // f6
cp = mods & shiftKey ? ESC "[29~" : ESC "[17~";
len = 5;
break;
case 0x62: // f7
cp = mods & shiftKey ? ESC "[31~" : ESC "[18~";
len = 5;
break;
case 0x64: // f8
cp = mods & shiftKey ? ESC "[32~" : ESC "[19~";
len = 5;
break;
case 0x65: // f9
cp = mods & shiftKey ? ESC "[33~" : ESC "[20~";
len = 5;
break;
case 0x6d: // f10
cp = mods & shiftKey ? ESC "[34~" : ESC "[21~";
len = 5;
break;
case 0x67: // f11
cp = ESC "[23~";
len = 5;
break;
case 0x6f: // f12
cp = ESC "[24~";
len = 5;
break;
default: len = 0; cp = NULL;
}
}
else if (mods & controlKey)
{
cp = (char *)&event->message;
len = 1;
}
else
{
switch(key)
{
case 0x0d:
cp = "\r\n";
len = 2;
break;
case 0x08: // <--- arrow
cp = ESC "[D";
len = 3;
break;
case 0x0A: // down arrow
cp = ESC "[B";
len = 3;
break;
case 0x0B: // up arrow
cp = ESC "[A";
len = 3;
break;
case 0x15: // ---> arrow
cp = ESC "[C";
len = 3;
break;
// backspace to delete char
case 0x7f:
cp = "\x08";
len = 1;
break;
default:
cp = (char *)&event->message;
len = 1;
}
}
if (len) TCPIPWriteTCP(ipid, cp, len, false, false);
}

View File

@ -1,4 +1,4 @@
#include "Marinetti.h"
#include <tcpip.h>
#include <Locator.h>
#include <Memory.h>
#include <texttool.h>
@ -22,16 +22,11 @@
#include "telnet.h"
extern void ScrollRegion(word,word);
extern void ScrollDown(void);
extern void PrintChar(int the_char, unsigned int andMask);
extern void ClearScreen(void);
extern void ClearScreenFrom(int Y);
extern void ClearLine(int Y);
extern void ClearLineFrom(int Y, int X);
extern void GrafOn(void) inline(0x0a04, 0xe10000);
extern void GrafOff(void) inline(0x0b04, 0xe10000);
extern pascal void GrafOn(void) inline(0x0a04, dispatcher);
extern pascal void GrafOff(void) inline(0x0b04, dispatcher);
extern pascal void SetColorTable(Word, ColorTable) inline(0x0E04,dispatcher);
extern pascal void SysBeep2(Word) inline(0x3803,dispatcher);
extern pascal void SetAllSCBs(Word) inline(0x1404,dispatcher);
@ -39,26 +34,30 @@ extern pascal void SetMasterSCB(Word) inline(0x1604,dispatcher);
extern pascal void InitColorTable(ColorTable) inline(0x0D04,dispatcher);
Word MyID;
Word __gno;
Word modeTelnet = 1;
Word flagEcho = 1; // if *I* echo
void init_ansi(void);
Word ResolveHost(const char *name, cvtRecPtr cvt);
Word GetChar(word ipid, unsigned char *c);
Word UngetChar(char c);
int WaitForStatus(word ipid, word status);
void display_str(const char *txt, word cnt);
void display_pstr(const char *);
void display_cstr(const char *);
void display_err(Word);
void send_event(EventRecord *, Word);
extern void telnet_init(void);
extern void telnet_process(void);
extern void vt100_init(void);
extern void vt100_process(const unsigned char *buffer, unsigned buffer_size);
extern void vt100_event(EventRecord *event);
Word MyID;
Word __gno;
#define ESC "\x1b"
#pragma databank 1
@ -67,25 +66,17 @@ void send_event(EventRecord *, Word);
* called to print connect/disconnect messages from
* Marinetti. msg is a pstring.
*/
void printCallBack(const char *msg)
{
word i;
i = *msg;
if (i)
{
display_str("\x1b" "[1m", 4); // bold on
display_str(msg + 1, i); // show the string
display_str("\x1b" "[0m\n\r", 6); // bold off
void printCallBack(const char *msg) {
if (msg && *msg) {
display_cstr(ESC "\n\r[1m"); // bold on
display_pstr(msg); // show the string
display_cstr(ESC "[0m\n\r"); // bold off
}
}
#pragma databank 0
extern word esc;
extern word Xpos;
extern word Ypos;
void init_screen(void)
{
static void screen_init(void) {
#define WHITE 0x0fff
#define YELLOW 0x0ff0
#define BLACK 0x0000
@ -115,8 +106,8 @@ static ColorTable ct =
BLUE,
GREEN,
};
int i;
unsigned i;
// linearize memory, disable shadowing.
asm
@ -138,147 +129,48 @@ int i;
SetMasterSCB(0xc080);
SetAllSCBs(0xc080);
//InitColorTable(ct);
for (i = 0; i < 15; i++)
for (i = 0; i < 16; i++)
SetColorTable(i, ct);
ClearScreen();
GrafOn();
}
static char out_buffer[1024];
static char out_buffer_size = 0;
/*
* this is (more or less) a state machine.
* old state (and character) are passed in
* new state is returned.
*
* 0 = starting state.
*/
#define SB_IAC 0xfaff
Word do_iac(Word state, char c, Word ipid)
{
static char buffer[64];
static int cnt;
static word ipid = -1;
if (state == 0) return c == IAC ? IAC : 0;
// FF must be escaped as FF FF within SB.
// SB_IAC means the first FF has been processed.
if (state == SB)
{
if (c == IAC) return SB_IAC;
buffer[cnt++] = c;
return SB;
static void flush(void) {
if (out_buffer_size) {
TCPIPWriteTCP(ipid, out_buffer, out_buffer_size, true, false);
out_buffer_size = 0;
}
if (state == SB_IAC)
{
// it was an escaped FF
if (c == IAC)
{
buffer[cnt++] = IAC;
return SB;
}
else state = IAC; // process below...
}
if (state == IAC)
switch (c)
{
case DONT:
case DO:
case WONT:
case WILL:
return c;
case SB:
cnt = 0;
return c;
case SE:
// buffer is the data between
// IAC SB <...> IAC SE
if (buffer[0] == TELOPT_TTYPE
&& buffer[1] == TELQUAL_SEND)
{
static char msg[] =
{
IAC, SB, TELOPT_TTYPE, TELQUAL_IS,
'V', 'T', '1', '0', '0',
IAC, SE
};
TCPIPWriteTCP(ipid, msg,
sizeof(msg), true, false);
}
else if (buffer[0] == TELOPT_NAWS
&& buffer[1] == TELQUAL_SEND)
{
static char msg[] =
{
IAC, SB, TELOPT_NAWS,
0, 80, 0, 24,
IAC, SE
};
TCPIPWriteTCP(ipid, msg,
sizeof(msg), true, false);
}
else if (buffer[0] == TELOPT_TSPEED
&& buffer[1] == TELQUAL_SEND)
{
static char msg[] =
{
IAC, SB, TELOPT_TSPEED,
'9', '6', '0', '0', ',', '9', '6', '0', '0',
IAC, SE
};
TCPIPWriteTCP(ipid, msg,
sizeof(msg), true, false);
}
cnt = 0;
return 0;
default:
return 0;
}
if (state == DO)
{
buffer[0] = IAC;
buffer[1] = WONT;
buffer[2] = c;
if (c == TELOPT_TTYPE
|| c == TELOPT_SGA
|| c == TELOPT_NAWS
|| c == TELOPT_TSPEED)
{
buffer[1] = WILL;
}
TCPIPWriteTCP(ipid, buffer, 3, true, false);
}
if (state == DONT)
{
buffer[0] = IAC;
buffer[1] = WONT;
buffer[2] = c;
TCPIPWriteTCP(ipid, buffer, 3, true, false);
}
if (state == WILL || state == WONT)
{
buffer[0] = IAC;
buffer[1] = DONT;
buffer[2] = c;
if (c == TELOPT_ECHO)
{
buffer[1] = DO;
}
cnt = 3;
TCPIPWriteTCP(ipid, buffer, cnt, true, false);
}
return 0;
}
int main(int argc, char **argv)
{
void send(char *data, unsigned size) {
if (out_buffer_size + size > sizeof(out_buffer)) {
flush();
}
if (size > sizeof(out_buffer) / 2) {
TCPIPWriteTCP(ipid, data, size, true, false);
return;
}
memcpy(out_buffer + out_buffer_size, data, size);
out_buffer_size += size;
}
unsigned buffer_size;
unsigned char buffer[2048]; // 1500 is normal MTU size?
int main(int argc, char **argv) {
static EventRecord event;
static srBuffer sr;
static rrBuff rr;
static char str[16];
static cvtRec cvt;
@ -288,25 +180,15 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
Word iLoaded;
Word iConnected;
Word iStarted;
Word Quit;
Word ipid;
Handle dp;
int i;
Word key;
Word err;
unsigned char c;
//int fd;
int iac_state;
int ok;
Xpos = Ypos = 0;
esc = 0;
//iac = 0;
iLoaded = iStarted = iConnected = false;
__gno = false;
ipid = -1;
MyID = MMStartUp();
@ -321,58 +203,53 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
dp = NewHandle(0x0100, MyID,
attrBank | attrPage |attrNoCross | attrFixed | attrLocked,
0x000000);
HLock(dp);
EMStartUp((Word)*dp, 0x14, 0, 0, 0, 0, MyID);
if (argc != 2)
{
// todo: -vt52 -> start in vt52 mode (DECANM = 0)
// todo:keypad flag of some sort?
if (argc != 2) {
ErrWriteCString("Usage: marlene host[:port]\r\n");
goto _exit;
}
init_screen();
screen_init();
init_ansi();
vt100_init();
TCPIPStatus();
if (_toolErr)
{
display_str("\n\rLoading Marinetti...\n\r", 23);
if (_toolErr) {
display_cstr("Loading Marinetti...\n\r");
LoadOneTool(0x36, 0x200); //load Marinetti
if (_toolErr)
{
ErrWriteCString("Unable to load Marinetti\r\n");
if (_toolErr) {
display_cstr("Unable to load Marinetti.\r\n");
goto _exit;
}
iLoaded = true;
}
// Marinetti now loaded
if (!TCPIPStatus())
{
display_str("\n\rStarting Marinetti...\n\r", 24);
if (!TCPIPStatus()) {
display_cstr("Starting Marinetti...\n\r");
TCPIPStartUp();
iStarted = true;
}
if (!TCPIPGetConnectStatus())
{
display_str("\n\rConnecting Marinetti...\n\r", 26);
if (!TCPIPGetConnectStatus()) {
display_cstr("Connecting Marinetti...\n\r");
TCPIPConnect(printCallBack);
if (_toolErr)
{
ErrWriteCString("Unable to establish network connection\r\n");
if (_toolErr) {
display_cstr("Unable to establish network connection.\r\n");
goto _exit;
}
iConnected = true;
}
// marinetti is now connected
if (!ResolveHost(argv[1], &cvt))
{
ErrWriteCString("Unable to resolve address\r\n");
if (!ResolveHost(argv[1], &cvt)) {
display_cstr("Unable to resolve address.\r\n");
goto _exit;
}
@ -380,88 +257,69 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
ipid = TCPIPLogin(MyID, cvt.cvtIPAddress, cvt.cvtPort, 0, 0x0040);
display_str("\n\rConnecting to ", 16);
display_str(str, TCPIPConvertIPToCAscii(cvt.cvtIPAddress, str, 0));
TCPIPConvertIPToCASCII(cvt.cvtIPAddress, str, 0);
display_cstr("Connecting to ");
display_cstr(str);
display_cstr("...\n\r");
TCPIPOpenTCP(ipid);
// wait for the connection to occur.
while (1)
{
ok = WaitForStatus(ipid, TCPSESTABLISHED);
if (ok > 0) display_err(ok);
if (ok != 0) goto _exit2;
display_cstr("Connected.\n\r");
telnet_init();
//fd = open ("tcp.log", O_TRUNC | O_WRONLY | O_CREAT, 0777);
for(;;) {
static rrBuff rr;
TCPIPPoll();
err = TCPIPStatusTCP(ipid, &sr);
if (err)
{
rr.rrBuffCount = 0;
err = TCPIPReadTCP(ipid, 0, (Ref)buffer, sizeof(buffer), &rr);
// tcperrConClosing and tcperrClosing aren't fatal.
buffer_size = rr.rrBuffCount;
if (buffer_size) err = 0;
if (err) {
if (err == tcperrConClosing || err == tcperrClosing)
display_cstr("\r\nTCP Connection Closed.\r\n");
else
display_err(err);
goto _exit1;
}
if (sr.srState == tcpsESTABLISHED) break;
//if (sr.srState == tcpsCLOSED) goto _exit1;
GetNextEvent(keyDownMask | autoKeyMask ,&event);
if (event.what != keyDownEvt) continue;
if (!(event.modifiers & appleKey)) continue;
if ((Word)event.message == '.') goto _exit1;
if ((Word)event.message == 'q') goto _exit1;
if ((Word)event.message == 'Q') goto _exit1;
if (buffer_size) {
telnet_process();
}
display_str("\n\rConnected\n\r", 13);
//fd = open ("tcp.log", O_TRUNC | O_WRONLY | O_CREAT, 0777);
Quit = false;
iac_state = 0;
while (!Quit)
{
TCPIPPoll();
// check for incoming data...
// 0xffff = no data
// 0 = data
// otherwise = marinetti error.
err = GetChar(ipid, &c);
if (err && (err != 0xffff))
{
display_err(err);
Quit++;
continue;
}
if (err == 0)
{
//if (fd > 0) write(fd, &c, 1);
if (iac_state)
iac_state = do_iac(iac_state, c, ipid);
else if (modeTelnet && c == IAC)
iac_state = do_iac(0, c, ipid);
else display_str(&c, 1);
if (buffer_size) {
vt100_process(buffer, buffer_size);
}
GetNextEvent(keyDownMask | autoKeyMask, &event);
if (event.what != keyDownEvt) continue;
c = key = event.message;
if (event.what == keyDownEvt) {
if (event.modifiers & appleKey)
{
switch (key)
{
unsigned char key = event.message;
if (event.modifiers & appleKey) {
switch (key) {
case 'Q': // quit
case 'q':
Quit++;
goto _exit1;
break;
case 'V': // paste
case 'V': // paste...
case 'v':
break;
case 'Z': // suspend (gnome)
case 'z':
if (__gno)
{
if (__gno) {
static struct sgttyb sb;
static int err;
gtty(1,&sb);
@ -475,27 +333,29 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
}
break;
}
continue;
} else {
vt100_event(&event);
}
else send_event(&event, ipid);
}
flush();
}
_exit1:
//if (fd > 0) close(fd);
TCPIPCloseTCP(ipid);
TCPIPPoll(); // wait until closed...
TCPIPLogout(ipid);
flush();
// be nice and
while (GetNextEvent(keyDownMask | autoKeyMask, &event));
display_str("\n\rPress any key to exit.\n\r", 26);
while (!GetNextEvent(keyDownMask | autoKeyMask, &event));
display_cstr("\n\rClosing TCP Connection...\n\r");
TCPIPCloseTCP(ipid);
WaitForStatus(ipid, TCPSCLOSED);
_exit2:
_exit:
if (ipid != -1) TCPIPLogout(ipid);
if (iConnected)
TCPIPDisconnect(false, printCallBack);
@ -506,10 +366,17 @@ _exit:
UnloadOneTool(0x36);
// flush q
while (GetNextEvent(keyDownMask | autoKeyMask, &event)) ;
display_cstr("\n\rPress any key to exit.\n\r");
while (!GetNextEvent(keyDownMask | autoKeyMask, &event)) ;
EMShutDown();
DisposeHandle(dp);
GrafOff();
TextShutDown();
QuitGS(&qDCB);
return 0;
}

View File

@ -1,5 +1,5 @@
PROG = fctelnet
OBJS = fctelnet.o vt100.o ansi.o chars.o marinetti.o display.o
OBJS = fctelnet.o vt100.o telnet.o ansi.o chars.o marinetti.o display.o
OPTIMIZE *= 79
@ -16,6 +16,7 @@ vt100.o: vt100.c
ansi.o: ansi.asm
chars.o: chars.asm
marinetti.o: marinetti.c
telnet.o: telnet.c
clean:
$(RM) -f *.o *.root *.a *.r

View File

@ -60,25 +60,21 @@ EventRecord event;
else
{
TCPIPDNRNameToIP(pstr, &dnr);
if (_toolErr)
{
if (_toolErr) {
free(pstr);
return false;
}
while (dnr.DNRStatus == DNRPending)
{
while (dnr.DNRStatus == DNRPending) {
TCPIPPoll();
GetNextEvent(keyDownMask | autoKeyMask, &event);
if ((event.what == keyDownEvt)
&& (event.modifiers & appleKey)
&& ((Word)event.message == '.'))
{
&& ((Word)event.message == '.')) {
TCPIPCancelDNR(&dnr);
break;
}
}
if (dnr.DNRStatus == DNROK)
{
if (dnr.DNRStatus == DNROK) {
cvt->cvtIPAddress = dnr.DNRIPAddress;
cvt->cvtPort = port;
free(pstr);
@ -91,47 +87,25 @@ EventRecord event;
return false;
}
int WaitForStatus(word ipid, word status) {
static srBuffer sr;
EventRecord event;
Word err;
/*
* pcnt is the next place to store data
* bptr is the current ptr.
*/
static unsigned char buffer[1024];
static unsigned char pushback[1024];
static int pcnt = 0;
static int bcnt = 0;
static int bptr = 0;
static rrBuff rr;
Word GetChar(word ipid, unsigned char *c)
{
word err;
*c = 0;
if (pcnt)
{
*c = pushback[--pcnt];
return 0;
}
if (!bcnt)
{
err = TCPIPReadTCP(ipid, 0, (Ref)buffer, 1024, &rr);
bcnt = rr.rrBuffCount;
bptr = 0;
for(;;) {
TCPIPPoll();
err = TCPIPStatusTCP(ipid, &sr);
if (err) return err;
if (sr.srState == status) return 0;
GetNextEvent(keyDownMask | autoKeyMask, &event);
if (event.what != keyDownEvt) continue;
if (!(event.modifiers & appleKey)) continue;
if ((Word)event.message == '.') return -1;
//if ((Word)event.message == 'q') return -1;
//if ((Word)event.message == 'Q') return -1;
}
if (bcnt)
{
bcnt--;
*c = buffer[bptr++];
return 0;
}
return 0xffff;
}
void UngetChar(char c)
{
pushback[pcnt++] = c;
}

929
vt100.c
View File

@ -1,45 +1,95 @@
/*
* This handles vt100 commands.
*
*/
#pragma noroot
#include <Types.h>
#include <texttool.h>
#include <Event.h>
#include <ctype.h>
// handle vt100 escape codes
extern void ReverseScrollRegion(word, word);
extern void ScrollRegion(word, word);
extern void ScrollDown(void);
extern void PrintChar(int the_char, unsigned int andMask);
#define ESC "\x1b"
#define send_str(x) send((const unsigned char *)x, sizeof(x)-1)
extern void send(const unsigned char *, unsigned);
extern unsigned char buffer[];
extern unsigned buffer_size;
extern void ReverseScrollRegion(unsigned, unsigned);
extern void ScrollRegion(unsigned, unsigned);
extern void PrintChar(unsigned x, unsigned y, unsigned the_char, unsigned andMask, unsigned eorMask);
extern void ClearScreen(void);
extern void ClearScreenFrom(int Y);
extern void ClearLine(int Y);
extern void ClearLineFrom(int Y, int X);
extern void ClearScreen2(unsigned start, unsigned end);
extern void ClearLine(unsigned line);
extern void ClearLine2(unsigned line, unsigned start, unsigned end);
extern void SetCursor(unsigned x, unsigned y);
extern pascal void SysBeep2(Word) inline(0x3803,dispatcher);
word Xpos;
word Ypos;
static word __pos[2]; // saved cursor position
word __scroll[2];
word and_mask;
word xor_mask;
int __x;
int __y;
void init_ansi(void)
static unsigned saved_cursor[2]; // saved cursor position
static unsigned window[2];
static unsigned and_mask;
static unsigned xor_mask;
static unsigned DECANM = 1;
static unsigned DECAWM = 1;
static unsigned DECCKM = 0;
static unsigned DECKPAM = 0;
static unsigned DECCOLM = 80;
static unsigned DECOM = 0;
static unsigned DECSCNM = 0;
static unsigned LNM = 0;
unsigned tabs[9] = { 0 };
static unsigned parms[10];
static unsigned parm_count;
static unsigned private;
static unsigned state;
void vt100_init(void)
{
Xpos = 0;
Ypos = 0;
__pos[0] = 0;
__pos[1] = 0;
__scroll[0] = 0;
__scroll[1] = 23;
__x = 0;
__y = 0;
saved_cursor[0] = 0;
saved_cursor[1] = 0;
window[0] = 0;
window[1] = 23;
and_mask = 0xffff;
xor_mask = 0x0000;
DECANM = 1;
DECAWM = 1;
DECCKM = 0;
DECKPAM = 0;
DECCOLM = 80;
DECOM = 0;
DECSCNM = 0;
LNM = 0;
tabs[0] = 0x8080;
tabs[1] = 0x8080;
tabs[2] = 0x8080;
tabs[3] = 0x8080;
tabs[4] = 0x0080;
parms[0] = 0;
parm_count = 0;
private = 0;
state = 0;
}
//not yet added:
//esc[6n - return sursor position report
//esc[m - set graphics mode
//
#if 0
void dump(const char * buffer, word cnt, int impl)
{
int i;
@ -57,165 +107,114 @@ char c;
}
WriteCString("\r\n");
}
#endif
static void cursor_left(void){
unsigned n = parms[0];
if (n == 0) n = 1;
void handle_ansi(word code, char *buffer, word cnt)
{
word i;
word r0;
word r1;
//word r2;
//word r3;
word pop = 0;
if (buffer[1] == '[')
{
i = 2;
switch (code)
{
case 'A': //cursor up
// esc[#A
r0 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'A') pop = 1;
else
{
if (!r0) r0 = 1;
Ypos -= r0;
if (Ypos < __scroll[0]) Ypos = __scroll[0];
//if (Ypos > r0) Ypos -= r0;
//else Ypos = 0;
__x -= n;
if (__x < 0) __x = 0;
}
break;
case 'B': //cursor down
// esc[#B
r0 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'B') pop = 1;
else
{
if (!r0) r0 = 1;
Ypos += r0;
if (Ypos > __scroll[1]) Ypos = __scroll[1];
//if (Ypos + r0 > 24) Ypos = 24;
//else Ypos += r0;
static void cursor_right(void){
unsigned n = parms[0];
if (n == 0) n = 1;
// edge case
if (__x == 80) return;
__x += n;
if (__x >= 80) n = 79;
}
break;
case 'C': // cursor forward
// esc[#C
r0 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'C') pop = 1;
else
{
if (!r0) r0 = 1;
Xpos += r0;
if (Xpos > 79) Xpos = 79;
static void cursor_up(void) {
unsigned n = parms[0];
if (n == 0) n = 1;
/* can't escape scrolling region */
if (__y >= window[0]) {
__y -= n;
if (__y < window[0]) __y = window[0];
} else {
__y -= n;
if (__y < 0) __y = 0;
}
break;
case 'D': // cursor backward
// esc[#D
r0 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'D') pop = 1;
else
{
if (!r0) r0 = 1;
Xpos -= r0;
if (Xpos < 0) Xpos = 0;
}
break;
case 'H': //cursor position
case 'f':
//esc[#;#H or esc[;H or esc[H <?>
//esc[#;#f or esc[;f or esc[f <?>
if (cnt > 3)
{
r0 = r1 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i++] != ';') pop = 1;
else while (isdigit(buffer[i]))
r1 = r1 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'H' && buffer[i] != 'f') pop = 1;
if (r0 == 0) r0++;
if (r1 == 0) r1++;
}
else r0 = r1 = 1;
if (!pop)
{
Xpos = r1 - 1;
Ypos = r0 - 1;
}
break;
case 'J':
if (cnt == 3) //clear c
{
ClearLineFrom(Ypos, Xpos);
if (Ypos < 23)
ClearScreenFrom(Ypos+1);
}
else if (cnt == 4 && buffer[2] == '2')
{
ClearScreen();
//Ypos = 0; // cursor position unchanged
//Xpos = 0;
static void cursor_down(void) {
unsigned n = parms[0];
if (n == 0) n = 1;
/* can't escape scrolling region */
if (__y <= window[1]) {
__y += n;
if (__y > window[1]) __y = window[1];
} else {
__y += n;
if (__y >= 24) __y = 23;
}
}
break;
case 'K': //erase from cursor to end-of-line (including cursor)
if (cnt == 3) // no parms
ClearLineFrom(Ypos, Xpos);
else dump(buffer, cnt,1);
break;
static void cursor_position(void) {
unsigned y = parms[0];
unsigned x = parms[1];
case 'L': // erase current line and
// scroll preceding lines down 1
// esc[#L
ReverseScrollRegion(Ypos, __scroll[1]);
break;
if (y) --y;
if (x) --x;
case 'M': // erase current line and
//scroll following lines up 1
// esc[#M
ScrollRegion(Ypos, __scroll[1]);
break;
__x = x;
if (x >= 80) x = 79;
if (DECOM) {
__y = window[0] + y;
if (__y > window[1]) __y = window[1];
} else {
__y = y;
if (__y >= 24) __y = 23;
}
}
case 's': // save cursor position
__pos[0] = Xpos;
__pos[1] = Ypos;
break;
static void cursor_position_vt52(void) {
unsigned y = parms[0];
unsigned x = parms[1];
case 'u': // restore cursor position
Xpos = __pos[0];
Ypos = __pos[1];
break;
if (y) --y;
if (x) --x;
// not yet added!
case 'n':
break;
case 'm':
while (1)
{
r0 = 0;
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
// if x or y are out of range, do not move them.
switch(r0)
{
if (x < 80) __x = x;
if (DECOM) {
y = window[0] + y;
if (y <= window[1]) __y = y;
} else {
if (y < 24) __y = y;
}
}
static void scrolling_region(void) {
unsigned top = parms[0];
unsigned bottom = parms[1];
if (top) --top;
if (!bottom) bottom = 24;
--bottom;
if (top < bottom && bottom < 24) {
window[0] = top;
window[1] = bottom;
__y = DECOM ? top : 0;
__x = 0;
}
}
static void set_attributes(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 0: // all attributes off
and_mask = 0xffff;
xor_mask = 0x0000;
@ -230,50 +229,574 @@ word pop = 0;
case 7://reverse video
xor_mask = 0xffff;
break;
case 8://cancel
and_mask = 0x0000;
break;
}
if (buffer[i] == ';') i++;
else if (buffer[i] == 'm') break;
}
}
static void set_mode(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 1: if (private) DECCKM = 1; break;
case 2: if (private) DECANM = 1; break;
case 3: if (private) DECCOLM = 132; break;
case 6: if (private) { DECOM = 1; __x = 0; __y = window[0]; } break;
case 7: if (private) DECAWM = 1; break;
case 20: if (!private) LNM = 1; break;
}
}
}
static void reset_mode(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 1: if (private) DECCKM = 0; break;
case 2: if (private) DECANM = 0; break;
case 3: if (private) DECCOLM = 80; break;
case 6: if (private) { DECOM = 0; __x = 0; __y = 0; } break;
case 7: if (private) DECAWM = 0; break;
case 20: if (!private) LNM = 0; break;
}
}
}
static void save_cursor(void) {
saved_cursor[0] = __y;
saved_cursor[1] = __x;
}
static void restore_cursor(void) {
__y = saved_cursor[0];
__x = saved_cursor[1];
// vt220+ also saves/restores DECAWM and DECOM...
}
static void tab(void) {
unsigned chunk;
unsigned mask;
if (__x >= 80) return;
__x = __x + 1;
chunk = __x >> 0x4;
mask = 1 << (__x & 0x0f);
while (chunk < 5) {
unsigned bits = tabs[chunk];
if (!bits) {
__x += 16;
} else {
do {
if (bits & mask) return;
++__x;
mask <<= 1;
} while (mask);
}
mask = 1;
++chunk;
}
// nothing... go to right margin.
__x = 79;
}
static void set_tab(void) {
unsigned chunk;
unsigned mask;
if (__x >= 80) return;
chunk = __x >> 0x04;
mask = 1 << (__x & 0x0f);
tabs[chunk] |= mask;
}
static void clear_tabs(void) {
unsigned i;
unsigned chunk;
unsigned mask;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 0:
if (__x < 80) {
chunk = __x >> 0x04;
mask = 1 << (__x & 0x0f);
tabs[chunk] &= ~mask;
}
break;
case 5:
tabs[0] = 0;
tabs[1] = 0;
tabs[2] = 0;
tabs[3] = 0;
tabs[4] = 0;
break;
}
}
case 'r': // #;#r == set scrolling region
// esc[#;#r
r0 = r1 = 0;
}
while (isdigit(buffer[i]))
r0 = r0 * 10 + (buffer[i++] - '0');
if (buffer[i++] != ';') pop = 1;
else while (isdigit(buffer[i]))
r1 = r1 * 10 + (buffer[i++] - '0');
if (buffer[i] != 'r') pop = 1;
if (r0 && r1 && !pop)
static void linefeed(void) {
if (__y == window[1]) {
ScrollRegion(window[0], window[1]+1);
} else ++__y;
}
static void reverse_linefeed(void) {
if (__y == window[0]) {
ReverseScrollRegion(window[0], window[1]+1);
} else --__y;
}
static void erase_line(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 0:
ClearLine2(__y, __x, 80);
break;
case 1:
ClearLine2(__y, 0, __x + 1);
break;
case 2:
ClearLine(__y);
break;
}
}
}
static void erase_screen(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 0:
ClearLine2(__y, __x, 80);
ClearScreen2(__y + 1, 24);
break;
case 1:
ClearLine2(__y, 0, __x + 1);
ClearScreen2(0, __y);
break;
case 2:
ClearScreen();
break;
}
}
}
/* Device Status Report */
static void dsr(void) {
unsigned i;
for (i = 0; i < parm_count; ++i) {
switch(parms[i]) {
case 0: send_str(ESC "[0n"); break;
case 5: { /* cursor position */
static char buffer[16];
unsigned i = 15;
unsigned x = __x + 1;
unsigned y = __y + 1;
if (DECOM) y += window[0];
buffer[i--] = ']';
while(x) {
buffer[i--] = '0' + (x % 10);
x = x / 10;
}
buffer[i--] = ';';
while(y) {
buffer[i--] = '0' + (y % 10);
y = y / 10;
}
buffer[i--] = '[';
buffer[i] = 0x1b;
send(buffer + i, 16 - i);
break;
}
}
}
}
enum {
st_text,
st_escape,
st_escape_v52,
st_dca1,
st_dca2,
st_pound,
st_rparen,
st_lparen,
st_lbracket,
st_lbracket2,
st_parm,
};
static void do_control(char c) {
switch(c) {
case 005: /* ENQ */ break;
case 007: /* BEL */ SysBeep2(0); break;
case 010: /* BS */ if (__x) --__x; break;
case 011: /* HT */ tab(); break;
case 012: /* LF */
case 013: /* VT */
case 014: /* FF */
linefeed();
if (LNM) __x = 0;
break;
case 015: /* CR */ __x = 0; break;
case 016: /* SO */ break;
case 017: /* SI */ break;
case 021: /* XON */ break;
case 023: /* XOFF */ break;
case 030: /* CAN */
case 032: /* SUB */
state = st_text; break;
case 033: /* ESC */
switch (state) {
case st_text: state = DECANM ? st_escape : st_escape_v52; break;
case st_escape_v52:
case st_escape:
break;
default:
state = st_text;
}
break;
}
}
void vt100_process(const unsigned char *buffer, unsigned buffer_size) {
unsigned i;
for (i = 0; i < buffer_size; ++i) {
unsigned char c = buffer[i] & 0x7f;
if (c == 0 || c == 0x7f) continue;
if (c < 0x20) {
do_control(c);
continue;
}
switch (state) {
case st_text:
if (__x == 80) {
if (DECAWM) {
__x = 0;
if (__y == window[1]) {
ScrollRegion(window[0], window[1]+1);
} else ++__y;
} else {
__x = 79;
}
}
PrintChar(__x, __y, c, and_mask, xor_mask);
++__x;
break;
case st_escape:
switch (c) {
case '#': state = st_pound; break;
case '(': state = st_lparen; break;
case ')': state = st_rparen; break;
case '[': state = st_lbracket; break;
case 'E': /* next line */ __x = 0; /* drop through */
case 'D': /* index */ linefeed(); break;
case 'H': set_tab(); break;
case 'M': /* reverse index */ reverse_linefeed(); break;
case 'Z': send_str(ESC "[?1;0c"); break;
case '7': save_cursor(); break;
case '8': restore_cursor(); break;
case '=': DECKPAM = 1; break;
case '>': DECKPAM = 0; break;
case 'c': vt100_init(); ClearScreen(); break;
case '1': case '2': /* vt105 graphic stuff */ break;
default:
break;
}
if (state == st_escape) state = st_text;
break;
case st_pound:
/* #8 -> fill with Es */
state = st_text; break;
case st_lparen:
case st_rparen:
state = st_text; break;
break;
case st_lbracket:
private = 0;
parm_count = 0;
parms[0] = 0;
parms[1] = 0;
if (c == '?') { private = 1; state = st_parm; continue; }
if (isdigit(c)) {
parms[0] = c - '0';
state = st_parm;
break;
}
/* fall through */
case st_lbracket2:
lbracket2:
switch (c) {
case 'A': cursor_up(); break;
case 'B': cursor_down(); break;
case 'C': cursor_right(); break;
case 'D': cursor_left(); break;
case 'H': case 'f': cursor_position(); break;
case 'K': erase_line(); break;
case 'J': erase_screen(); break;
case 'c': if (parms[0] == 0) send_str(ESC "[?1;0c"); break;
case 'g': clear_tabs(); break;
case 'h': set_mode(); break;
case 'l': reset_mode(); break;
case 'm': set_attributes(); break;
case 'n': dsr(); break;
case 'r': scrolling_region(); break;
}
state = st_text;
break;
case st_parm:
if (isdigit(c)) {
parms[parm_count] *= 10;
parms[parm_count] += c - '0';
continue;
}
if (c == ';') {
if (parm_count < 10) ++parm_count;
parms[parm_count] = 0;
continue;
}
state = st_lbracket2;
goto lbracket2;
case st_escape_v52:
parm_count = 0;
parms[0] = 0;
if (c == 'Y') { state = st_dca1; break; }
switch (c) {
case 'A': cursor_up(); break;
case 'B': cursor_down(); break;
case 'C': cursor_right(); break;
case 'D': cursor_left(); break;
case 'F': case 'G': break; /* graphics mode */
case 'H': cursor_position(); break;
case 'I': reverse_linefeed(); break;
case 'J': erase_screen(); break;
case 'K': erase_line(); break;
case 'Z': send_str(ESC "/Z"); break;
case '=': DECKPAM = 1; break;
case '>': DECKPAM = 0; break;
case '<': DECANM = 1; break;
}
state = st_text;
break;
case st_dca1:
parms[0] = c - 0x20;
state = st_dca2;
break;
case st_dca2:
parms[1] = c - 0x20;
cursor_position_vt52();
state = st_text;
break;
}
}
}
//
// remap the iigs key to a vt100 code (if necessary) and send it out.
/*
* see Apple IIgs Toolbox Ref Volume 1, 3-22. (ADB codes)
*/
void vt100_event(EventRecord *event) {
unsigned char *cp;
int len = 0;
unsigned char key;
word mods;
key = event->message;
mods = event->modifiers;
/* iigs -> vt100
* clear -> PF1
* = -> PF2
* / -> PF3
* * -> PF4
* + -> -
* - -> ,
* . -> .
*
*
* win32 layout:
* numlock -> PF1
* / -> PF2
* * -> PF3
* - -> PF4
* shift+ -> -
* + -> ,
*
* ------
* option - , 1 2 3 4 to use normal keys as keypad.
*/
if (mods & keyPad) {
switch (key) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case '-':
case ',':
{
__scroll[0] = r0 - 1;
__scroll[1] = r1 - 1;
static unsigned char buffer[3] = { 0x1b, 'x', 'x' };
if (DECKPAM) {
cp = buffer; len = 3;
buffer[2] = key | 0x40;
buffer[1] = DECANM ? 'O' : '?';
} else {
len = 1;
cp = &key;
}
}
break;
case 0x0d: // enter
if (DECKPAM) {
cp = DECANM ? ESC "OM" : ESC "?M";
len = 3;
} else goto enter;
break;
case 0x7a: // f1
if (DECANM) { cp = ESC "OP"; len = 3; }
else { cp = ESC "P"; len = 2; }
break;
case 0x78: // f2
if (DECANM) { cp = ESC "OQ"; len = 3; }
else { cp = ESC "Q"; len = 2; }
break;
case 0x63: // f3
if (DECANM) { cp = ESC "OR"; len = 3; }
else { cp = ESC "R"; len = 2; }
break;
case 0x76: // f4
if (DECANM) { cp = ESC "OS"; len = 3; }
else { cp = ESC "S"; len = 2; }
break;
/* Real IIgs keyboard - clear = 0x18, =, /, * for PF1-4? */
case 0x18: // clear - PF1 on IIgs keyboard
if (0) {
if (DECANM) { cp = ESC "OP"; len = 3; }
else { cp = ESC "P"; len = 2; }
}
break;
case '=': // PF2 on IIgs keyboard
if (0) {
if (DECANM) { cp = ESC "OQ"; len = 3; }
else { cp = ESC "Q"; len = 2; }
}
break;
case '/': // PF3 on IIgs keyboard
if (0) {
if (DECANM) { cp = ESC "OR"; len = 3; }
else { cp = ESC "R"; len = 2; }
} else {
len = 1;
cp = &key;
}
break;
case '*': // PF4 on IIgs keyboard
if (0) {
if (DECANM) { cp = ESC "OS"; len = 3; }
else { cp = ESC "S"; len = 2; }
} else {
len = 1;
cp = &key;
}
break;
case '+':
// send as normal key.
len = 1;
cp = &key;
break;
default: return;
}
} else if (mods & controlKey) {
cp = &key;
len = 1;
} else {
switch(key) {
case 0x0d:
enter:
if (LNM) { cp = "\r\n"; len = 2; }
else { cp = "\r"; len = 1; }
break;
case 0x08: // <--- arrow
// todo -- cursor keys
if (DECANM) {
cp = DECCKM ? ESC "OD" : ESC "[D";
len = 3;
} else { cp = ESC "D"; len = 2; }
break;
case 0x0A: // down arrow
if (DECANM) {
cp = DECCKM ? ESC "OB" : ESC "[B";
len = 3;
} else { cp = ESC "B"; len = 2; }
break;
case 0x0B: // up arrow
if (DECANM) {
cp = DECCKM ? ESC "OA" : ESC "[A";
len = 3;
} else { cp = ESC "A"; len = 2; }
break;
case 0x15: // ---> arrow
if (DECANM) {
cp = DECCKM ? ESC "OC" : ESC "[C";
len = 3;
} else { cp = ESC "C"; len = 2; }
break;
// backspace to delete char
case 0x7f: // delete
cp = "\x08";
len = 1;
break;
default:
dump(buffer, cnt,1);
}
}
else
{
switch(buffer[1])
{
case 'M': // scroll screen down 1 line
ReverseScrollRegion(__scroll[0], __scroll[1]);
break;
case '=': // alt key pad
case '>': // normal key pad
break;
default:
dump(buffer, cnt,1);
if (key < 0x7f) {
cp = &key; len = 1;
}
}
}
if (len) send(cp, len);
}