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

384
display.c
View File

@ -1,54 +1,21 @@
#pragma noroot #pragma noroot
#pragma lint -1
#include <types.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <Event.h>
#include "Marinetti.h" #include "Marinetti.h"
#define ESC "\x1b"
extern void ScrollRegion(word,word); struct errlist {
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
{
Word Error; Word Error;
Word MsgLen; Word MsgLen;
const char *Msg; 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(1,"tcperrDeafDestPort"),
_ERR(2,"tcperrHostReset"), _ERR(2,"tcperrHostReset"),
_ERR(3,"tcperrConExists"), _ERR(3,"tcperrConExists"),
@ -63,346 +30,27 @@ struct errlist errors[] =
_ERR(0x0c,"tcperrConReset"), _ERR(0x0c,"tcperrConReset"),
_ERR(0x0d,"tcperrUserTimeout"), _ERR(0x0d,"tcperrUserTimeout"),
_ERR(0x0e,"tcperrConRefused"), _ERR(0x0e,"tcperrConRefused"),
{0,0,NULL}
}; };
void display_str(const char *buffer, word cnt);
void display_err(Word err) void display_err(Word err) {
{
struct errlist *l;
for (l = &errors[0]; l->Error; l++) if (err == 0 || err >= 0x0f) return;
if (l->Error == err) break; --err;
if (l->Error != err) return; vt100_process("\r\n" ESC "[1m", 6); // bold on
vt100_process(errors[err].Msg, errors[err].MsgLen);
display_str("\r\n" "\x1b" "[1m", 6); // bold on vt100_process(ESC "[0m", 4); // bold off
display_str(l->Msg, l->MsgLen); // show the string
display_str("\x1b" "[0m", 4); // bold off
} }
void display_cstr(const char *cstr) void display_cstr(const char *cstr) {
{
if (cstr && *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) 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 <Locator.h>
#include <Memory.h> #include <Memory.h>
#include <texttool.h> #include <texttool.h>
@ -22,16 +22,11 @@
#include "telnet.h" #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 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 pascal void GrafOn(void) inline(0x0a04, dispatcher);
extern void GrafOff(void) inline(0x0b04, 0xe10000); extern pascal void GrafOff(void) inline(0x0b04, dispatcher);
extern pascal void SetColorTable(Word, ColorTable) inline(0x0E04,dispatcher); extern pascal void SetColorTable(Word, ColorTable) inline(0x0E04,dispatcher);
extern pascal void SysBeep2(Word) inline(0x3803,dispatcher); extern pascal void SysBeep2(Word) inline(0x3803,dispatcher);
extern pascal void SetAllSCBs(Word) inline(0x1404,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); 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 ResolveHost(const char *name, cvtRecPtr cvt);
Word GetChar(word ipid, unsigned char *c); int WaitForStatus(word ipid, word status);
Word UngetChar(char c);
void display_str(const char *txt, word cnt);
void display_pstr(const char *); void display_pstr(const char *);
void display_cstr(const char *); void display_cstr(const char *);
void display_err(Word); 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 #pragma databank 1
@ -67,25 +66,17 @@ void send_event(EventRecord *, Word);
* called to print connect/disconnect messages from * called to print connect/disconnect messages from
* Marinetti. msg is a pstring. * Marinetti. msg is a pstring.
*/ */
void printCallBack(const char *msg) void printCallBack(const char *msg) {
{ if (msg && *msg) {
word i; display_cstr(ESC "\n\r[1m"); // bold on
i = *msg; display_pstr(msg); // show the string
if (i) display_cstr(ESC "[0m\n\r"); // bold off
{
display_str("\x1b" "[1m", 4); // bold on
display_str(msg + 1, i); // show the string
display_str("\x1b" "[0m\n\r", 6); // bold off
} }
} }
#pragma databank 0 #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 WHITE 0x0fff
#define YELLOW 0x0ff0 #define YELLOW 0x0ff0
#define BLACK 0x0000 #define BLACK 0x0000
@ -93,30 +84,30 @@ void init_screen(void)
#define GREEN 0x00f0 #define GREEN 0x00f0
#define RED 0x0f00 #define RED 0x0f00
static ColorTable ct = static ColorTable ct =
{ {
BLACK, // background BLACK, // background
RED, // underline / blink RED, // underline / blink
BLUE, // bold BLUE, // bold
GREEN, // foreground GREEN, // foreground
BLACK, BLACK,
RED, RED,
BLUE, BLUE,
GREEN, GREEN,
BLACK, BLACK,
RED, RED,
BLUE, BLUE,
GREEN, GREEN,
BLACK, BLACK,
RED, RED,
BLUE, BLUE,
GREEN, GREEN,
}; };
int i;
unsigned i;
// linearize memory, disable shadowing. // linearize memory, disable shadowing.
asm asm
@ -138,175 +129,66 @@ int i;
SetMasterSCB(0xc080); SetMasterSCB(0xc080);
SetAllSCBs(0xc080); SetAllSCBs(0xc080);
//InitColorTable(ct); //InitColorTable(ct);
for (i = 0; i < 15; i++) for (i = 0; i < 16; i++)
SetColorTable(i, ct); SetColorTable(i, ct);
ClearScreen(); ClearScreen();
GrafOn(); GrafOn();
} }
static char out_buffer[1024];
static char out_buffer_size = 0;
/* static word ipid = -1;
* 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;
if (state == 0) return c == IAC ? IAC : 0; static void flush(void) {
// FF must be escaped as FF FF within SB. if (out_buffer_size) {
// SB_IAC means the first FF has been processed. TCPIPWriteTCP(ipid, out_buffer, out_buffer_size, true, false);
if (state == SB) out_buffer_size = 0;
{
if (c == IAC) return SB_IAC;
buffer[cnt++] = c;
return SB;
} }
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) {
static EventRecord event;
static srBuffer sr; if (out_buffer_size + size > sizeof(out_buffer)) {
static rrBuff rr; flush();
static char str[16]; }
static cvtRec cvt; if (size > sizeof(out_buffer) / 2) {
static QuitRecGS qDCB = {2, 0, 0x4000}; 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 rrBuff rr;
static char str[16];
static cvtRec cvt;
static QuitRecGS qDCB = {2, 0, 0x4000};
Word iLoaded; Word iLoaded;
Word iConnected; Word iConnected;
Word iStarted; Word iStarted;
Word Quit;
Word ipid;
Handle dp; Handle dp;
int i;
Word key;
Word err; Word err;
unsigned char c; int ok;
//int fd;
int iac_state;
Xpos = Ypos = 0;
esc = 0;
//iac = 0;
iLoaded = iStarted = iConnected = false; iLoaded = iStarted = iConnected = false;
__gno = false; __gno = false;
ipid = -1;
MyID = MMStartUp(); MyID = MMStartUp();
@ -321,58 +203,53 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
dp = NewHandle(0x0100, MyID, dp = NewHandle(0x0100, MyID,
attrBank | attrPage |attrNoCross | attrFixed | attrLocked, attrBank | attrPage |attrNoCross | attrFixed | attrLocked,
0x000000); 0x000000);
HLock(dp);
EMStartUp((Word)*dp, 0x14, 0, 0, 0, 0, MyID); 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"); ErrWriteCString("Usage: marlene host[:port]\r\n");
goto _exit; goto _exit;
} }
init_screen(); screen_init();
init_ansi(); vt100_init();
TCPIPStatus(); TCPIPStatus();
if (_toolErr) if (_toolErr) {
{ display_cstr("Loading Marinetti...\n\r");
display_str("\n\rLoading Marinetti...\n\r", 23);
LoadOneTool(0x36, 0x200); //load Marinetti LoadOneTool(0x36, 0x200); //load Marinetti
if (_toolErr) if (_toolErr) {
{ display_cstr("Unable to load Marinetti.\r\n");
ErrWriteCString("Unable to load Marinetti\r\n");
goto _exit; goto _exit;
} }
iLoaded = true; iLoaded = true;
} }
// Marinetti now loaded // Marinetti now loaded
if (!TCPIPStatus()) if (!TCPIPStatus()) {
{ display_cstr("Starting Marinetti...\n\r");
display_str("\n\rStarting Marinetti...\n\r", 24); TCPIPStartUp();
TCPIPStartUp();
iStarted = true; iStarted = true;
} }
if (!TCPIPGetConnectStatus()) if (!TCPIPGetConnectStatus()) {
{ display_cstr("Connecting Marinetti...\n\r");
display_str("\n\rConnecting Marinetti...\n\r", 26);
TCPIPConnect(printCallBack); TCPIPConnect(printCallBack);
if (_toolErr) if (_toolErr) {
{ display_cstr("Unable to establish network connection.\r\n");
ErrWriteCString("Unable to establish network connection\r\n");
goto _exit; goto _exit;
} }
iConnected = true; iConnected = true;
} }
// marinetti is now connected // marinetti is now connected
if (!ResolveHost(argv[1], &cvt)) if (!ResolveHost(argv[1], &cvt)) {
{ display_cstr("Unable to resolve address.\r\n");
ErrWriteCString("Unable to resolve address\r\n");
goto _exit; goto _exit;
} }
@ -380,122 +257,105 @@ static QuitRecGS qDCB = {2, 0, 0x4000};
ipid = TCPIPLogin(MyID, cvt.cvtIPAddress, cvt.cvtPort, 0, 0x0040); ipid = TCPIPLogin(MyID, cvt.cvtIPAddress, cvt.cvtPort, 0, 0x0040);
display_str("\n\rConnecting to ", 16); TCPIPConvertIPToCASCII(cvt.cvtIPAddress, str, 0);
display_str(str, TCPIPConvertIPToCAscii(cvt.cvtIPAddress, str, 0)); display_cstr("Connecting to ");
display_cstr(str);
display_cstr("...\n\r");
TCPIPOpenTCP(ipid); TCPIPOpenTCP(ipid);
// wait for the connection to occur. // wait for the connection to occur.
while (1) ok = WaitForStatus(ipid, TCPSESTABLISHED);
{ if (ok > 0) display_err(ok);
TCPIPPoll(); if (ok != 0) goto _exit2;
err = TCPIPStatusTCP(ipid, &sr);
if (err)
{
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;
}
display_str("\n\rConnected\n\r", 13); display_cstr("Connected.\n\r");
telnet_init();
//fd = open ("tcp.log", O_TRUNC | O_WRONLY | O_CREAT, 0777); //fd = open ("tcp.log", O_TRUNC | O_WRONLY | O_CREAT, 0777);
Quit = false; for(;;) {
static rrBuff rr;
iac_state = 0;
while (!Quit)
{
TCPIPPoll(); TCPIPPoll();
// check for incoming data... rr.rrBuffCount = 0;
// 0xffff = no data err = TCPIPReadTCP(ipid, 0, (Ref)buffer, sizeof(buffer), &rr);
// 0 = data // tcperrConClosing and tcperrClosing aren't fatal.
// otherwise = marinetti error.
err = GetChar(ipid, &c);
if (err && (err != 0xffff)) buffer_size = rr.rrBuffCount;
{ if (buffer_size) err = 0;
display_err(err); if (err) {
Quit++; if (err == tcperrConClosing || err == tcperrClosing)
continue; display_cstr("\r\nTCP Connection Closed.\r\n");
else
display_err(err);
goto _exit1;
} }
if (err == 0) if (buffer_size) {
{ telnet_process();
//if (fd > 0) write(fd, &c, 1); }
if (iac_state) if (buffer_size) {
iac_state = do_iac(iac_state, c, ipid); vt100_process(buffer, buffer_size);
else if (modeTelnet && c == IAC)
iac_state = do_iac(0, c, ipid);
else display_str(&c, 1);
} }
GetNextEvent(keyDownMask | autoKeyMask, &event); GetNextEvent(keyDownMask | autoKeyMask, &event);
if (event.what != keyDownEvt) continue; if (event.what == keyDownEvt) {
c = key = event.message;
if (event.modifiers & appleKey) unsigned char key = event.message;
{
switch (key) if (event.modifiers & appleKey) {
{ switch (key) {
case 'Q': // quit case 'Q': // quit
case 'q': case 'q':
Quit++; goto _exit1;
break; break;
case 'V': // paste case 'V': // paste...
case 'v': case 'v':
break; break;
case 'Z': // suspend (gnome) case 'Z': // suspend (gnome)
case 'z': case 'z':
if (__gno) if (__gno) {
{ static struct sgttyb sb;
static struct sgttyb sb; static int err;
static int err; gtty(1,&sb);
gtty(1,&sb); sb.sg_flags &= ~RAW;
sb.sg_flags &= ~RAW; stty(1,&sb);
stty(1,&sb); GrafOff();
GrafOff(); Kkill(Kgetpid(), SIGSTOP, &err);
Kkill(Kgetpid(), SIGSTOP, &err); sb.sg_flags |= RAW;
sb.sg_flags |= RAW; stty(1,&sb);
stty(1,&sb); GrafOn();
GrafOn(); }
break;
} }
break;
} else {
vt100_event(&event);
} }
continue;
} }
else send_event(&event, ipid); flush();
} }
_exit1: _exit1:
//if (fd > 0) close(fd); flush();
TCPIPCloseTCP(ipid);
TCPIPPoll(); // wait until closed...
TCPIPLogout(ipid);
// be nice and display_cstr("\n\rClosing TCP Connection...\n\r");
while (GetNextEvent(keyDownMask | autoKeyMask, &event));
display_str("\n\rPress any key to exit.\n\r", 26); TCPIPCloseTCP(ipid);
while (!GetNextEvent(keyDownMask | autoKeyMask, &event)); WaitForStatus(ipid, TCPSCLOSED);
_exit2:
_exit: _exit:
if (ipid != -1) TCPIPLogout(ipid);
if (iConnected) if (iConnected)
TCPIPDisconnect(false, printCallBack); TCPIPDisconnect(false, printCallBack);
@ -506,10 +366,17 @@ _exit:
UnloadOneTool(0x36); 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(); EMShutDown();
DisposeHandle(dp);
GrafOff(); GrafOff();
TextShutDown(); TextShutDown();
QuitGS(&qDCB); QuitGS(&qDCB);
return 0;
} }

View File

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

View File

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

989
vt100.c

File diff suppressed because it is too large Load Diff