Compare commits

...

66 Commits
r1 ... master

Author SHA1 Message Date
Kelvin Sherlock a687a08b0b add command-L to toggle local/online
add command-Q to quit.

Since ROM 1 doesn't have _RemoveCDA in ROM, the control panel is only installed in ROM 3.
(TODO -- check at runtime in case launched from GS/OS)
2022-02-07 10:34:10 -05:00
Kelvin Sherlock 90e807ba46 reset - enable auto-repeat. 2022-01-30 22:29:49 -05:00
Kelvin Sherlock 2c432b3fc1 command-L toggles local/online mode. 2022-01-30 22:29:33 -05:00
Kelvin Sherlock dcbafc2999 commentary. 2022-01-30 15:52:38 -05:00
Kelvin Sherlock 80edada6e9 DECCOLM 132 support (partial).
I still don't support 132 mode but switching clears the screen, homes the cursor,
and clears any scrolling regions, so it's necessary for passing the vttest suite.
2022-01-30 15:52:32 -05:00
Kelvin Sherlock 04113c4725 Rom 1 _InstallCDA call clobbers some direct page locations.
This is fixed in ROM 3 (or via the TS patches if you boot GS/OS).
Workaround it by switching to DPAGE 0.

Also, allocate memory (since I originally thought that might be relevant).
2022-01-30 15:50:54 -05:00
Kelvin Sherlock 1f7e3b3b4c to make life easier for emacs, backspace will send $7f instead of $08.
command-backspace will send $08 (as will Control-H).
If you have an extended keyboard, delete will also send $08.
(mame uses delete to toggle keyboard mode so it's not yet tested).
2022-01-29 15:43:08 -05:00
Kelvin Sherlock 18daf26568 add DECARM, x, and y to the CDA 2022-01-29 15:41:14 -05:00
Kelvin Sherlock 60259531d5 DECARM - auto repeat support. defaults to auto-repeat on.
n.b. - mame does not currently set the repeat bit in the keymod reg so it's not well tested.
2022-01-29 14:24:55 -05:00
Kelvin Sherlock 22d1c744bb auto-wrap wasn't advancing the cursor after wrapping. 2022-01-29 11:56:15 -05:00
Kelvin Sherlock c15ae6ac4c add DECREQTPARM support, fix a bug with DA/Device attributes response. 2022-01-29 11:23:41 -05:00
Kelvin Sherlock 9dfc3cc7c9 saving the cursor also saves the SGR 2022-01-29 10:43:09 -05:00
Kelvin Sherlock 71993497ea $7f (delete) character is a null char. drop it (and $00) when checking the read queue. 2022-01-28 23:21:13 -05:00
Kelvin Sherlock acfe94a4c7 command keys:
* command 1 - command 4 equivalent to PF1 - PF4
* command-delete = backspace (0x7f)
* command-return = linefeed (0x0a)
2022-01-28 22:25:33 -05:00
Kelvin Sherlock 9cf52cb34e vttest fixes
1. control-space should generate a null character
2. vt52 application mode keys were off.
2022-01-28 22:12:37 -05:00
Kelvin Sherlock 186d71de47 print the cancel character for ^X and ^Z.
based on testing, this applies to vt52 and vt100.
use the current x pos to choose between $56 and $57 (mouse text checkerboard)
so multiple characters look pretty.
2022-01-17 13:13:07 -05:00
Kelvin Sherlock 4eb578b94e merge hexdump code 2022-01-17 13:11:00 -05:00
Kelvin Sherlock c2d3ee0f28 show all SGR (inverse, bold, underscore, blink) as inverted. 2022-01-15 16:36:28 -05:00
Kelvin Sherlock 900dccb3d1 head/tail swapped. 2022-01-15 16:31:38 -05:00
Kelvin Sherlock ea6ee76a9d fix CDA paging 2022-01-15 16:15:05 -05:00
Kelvin Sherlock 1cc501e72a buffer outgoing keystrokes
this also adds a hexdump in the CDA for the outgoing buffer
in local mode, "incoming" data is read from the outgoing buffer.
Currently data is (potentially) sent on each run of the main loop.
I thought about using the TX empty interrupt but it still needs to be
kicked off at some point (unlike incoming data)
2022-01-15 15:59:29 -05:00
Kelvin Sherlock f486bbfb1b don't re-enable the cursor blink while there is still pending modem data. this improves performance and visuals. 2022-01-09 19:38:25 -05:00
Kelvin Sherlock 8fa8d8d1fa move modem q variables to the direct page, adjust CDA hexdump to show most recent 16*8 bytes. 2022-01-08 22:26:34 -05:00
Kelvin Sherlock 02c6e1271c cda print_number - support for 0-255 2021-12-19 20:16:52 -05:00
Kelvin Sherlock 279f424cee cda - improve ssc register display 2021-12-06 20:14:35 -05:00
Kelvin Sherlock e1639db6e4 cda - display SCC registers. 2021-12-05 22:47:40 -05:00
Kelvin Sherlock 6f055a1ef9 update cursor save/cursor restore to also save the DECOM settings. tested with vt100 (mame) 2021-11-28 15:46:29 -05:00
Kelvin Sherlock cd1397e3cf fixed cursor position report
- verified DECOM behavior
- was missing [ write.
2021-11-28 14:45:03 -05:00
ksherlock 3e53d52226
Create README.md 2021-10-26 08:16:26 -04:00
Kelvin Sherlock a4fc8a1a19 cda bugs 2021-10-25 14:30:50 -04:00
Kelvin Sherlock 260023c33b fix bug with scrolling region. frotz now works... 2021-10-25 14:30:42 -04:00
Kelvin Sherlock 0d64af63d2 move direct page so it's accessible to CDA
CDA shows direct page variables
move SCC setup to be table-based
esc c reset support.
2021-10-25 13:17:47 -04:00
Kelvin Sherlock c470e945ec clean up link file a bit. 2021-10-24 21:47:29 -04:00
Kelvin Sherlock 77ec62a091 at 2.6mhz, 9600baud, I was experiencing lost characters during line scroll or screen clear. To compensate, replace modem polling with asynchronous/interrupt-based code. Up to 256 bytes of modem data can be buffered.
Also, enable modem, vt100 as default

Also, erase screen before switching to 80-column mode to prevent unsightly flashes.
2021-10-24 20:13:06 -04:00
Kelvin Sherlock 5a1f0f6ffc finish cda box. 2021-10-23 14:53:35 -04:00
Kelvin Sherlock 26ceb7aa50 start of a CDA to control vt100 parameters. 2021-10-23 12:25:20 -04:00
Kelvin Sherlock 42d38bfa77 typo. 2021-10-23 12:24:36 -04:00
Kelvin Sherlock f13947a9c2 reformat scc magic constants 2021-10-17 19:33:28 -04:00
Kelvin Sherlock 892aed32f4 commentary. 2021-10-17 16:51:41 -04:00
Kelvin Sherlock 31394e84c4 backspace in column 0 was falling through to tab. 2021-10-03 16:09:39 -04:00
Kelvin Sherlock 6c8da61822 ^G beep support. uses ensoniq to match the vt100 beep. 2021-10-03 16:09:20 -04:00
Kelvin Sherlock 8d0f3b6496 use iigs equates 2021-10-03 16:08:23 -04:00
Kelvin Sherlock 2ae6964664 clobbering the screen holes is bad. The slot 4 mouse code stores config data in the screen holes. on ROM 1, ESC#8 (fill screen with 'E') would cause the slot 4 mouse to enable ADB mouse interrupts, which causes ProDOS to eventually error out with 255 unclaimed interrupts. 2021-09-27 17:55:10 -04:00
Kelvin Sherlock 2a8a409194 inverse support 2021-09-27 17:53:13 -04:00
Kelvin Sherlock 11d62bb333 disable cursor blink when CDA is active. 2021-09-26 19:46:10 -04:00
Kelvin Sherlock 39429e4e09 saved cursor 2021-09-26 19:45:44 -04:00
Kelvin Sherlock 9343302f9b status report (untested and incomplete) 2021-09-26 19:44:51 -04:00
Kelvin Sherlock dbf4e07b95 save restore cursor 2021-09-26 19:44:12 -04:00
Kelvin Sherlock 74d0ee7f07 based on testing, will not advance to column 80 unless DECAWM is set. 2021-09-25 21:28:12 -04:00
Kelvin Sherlock 45aea0293f arrows, screen erasing, region fixes 2021-09-25 21:27:48 -04:00
Kelvin Sherlock 7fbd54e791 csi wasn't resetting state at the end. 2021-09-25 13:57:27 -04:00
Kelvin Sherlock 121673badb bug fixes. 2021-09-25 11:05:06 -04:00
Kelvin Sherlock 63cac578bd vt52 might be more or less complete now. 2021-09-24 21:52:42 -04:00
Kelvin Sherlock 73722a2c02 enable cursor, some fixes 2021-09-23 23:43:25 -04:00
Kelvin Sherlock 1b2ac595d7 fixes 2021-09-23 17:16:10 -04:00
Kelvin Sherlock e9e969a56b tab/keypad fixes 2021-09-21 23:40:22 -04:00
Kelvin Sherlock 6abf2574e3 fixes. 2021-09-21 22:35:33 -04:00
Kelvin Sherlock dfdf608018 more code 2021-09-20 22:45:02 -04:00
Kelvin Sherlock d97b32a699 --ctrl, --upper, --lower, --digit flags. 2021-09-19 18:49:52 -04:00
Kelvin Sherlock 9c526bd762 more stuff 2021-09-19 18:49:29 -04:00
Kelvin Sherlock 6e0de284e0 more vt100 code
to handle DECAWM mode, bit 7 of x ($80 + 79) indicates a wrap is imminent, as opposed to just being in column 79. backspace, etc will drop back to column 78 (based on testing).
2021-09-17 19:50:56 -04:00
Kelvin Sherlock 42bef68d80 tab logic was off by one at the end. 2021-09-08 23:34:27 -04:00
Kelvin Sherlock 073813c199 more updates. 2021-09-05 17:04:52 -04:00
Kelvin Sherlock 1eaaf3aaaf more csi stuff 2021-09-04 20:27:42 -04:00
Kelvin Sherlock 492a3bfbec adding everything in progress, regardless of state. 2021-09-04 17:52:35 -04:00
Kelvin Sherlock dbda9761c5 full keypad support 2021-04-19 16:46:01 -04:00
21 changed files with 5873 additions and 34 deletions

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# itty-bitty-vtty
A vt100 emulator for the Apple IIgs
vt100, modem port, 9600 baud, 8-N-1 (MAME defaults)
A vt100 emulator based on The [User Guide](https://www.vt100.net/docs/vt100-ug/contents.html). Underspecified behavior is tested with MAME's vt100 emulation.
Unimplemented due to hardware limitations:
* alternate character sets
* graphic rendition (except plain/reverse)
* 132-column mode

41
apple2gs.equ.S Normal file
View File

@ -0,0 +1,41 @@
CLR80VID equ $c00c
SET80VID equ $c00d
CLRALTCHAR equ $c00e
SETALTCHAR equ $c00f
TXTSET equ $c051
KEYMOD equ $c025
KEYSTROBE equ $c010
KBD equ $c000
VGCINT equ $c023
SCANINT equ $c032
RDCARDRAM equ $c003
RDMAINRAM equ $c002
WRCARDRAM equ $c005
WRMAINRAM equ $c004
* modem port / printer port
SCCBREG equ $c038
SCCAREG equ $c039
SCCBDATA equ $c03a
SCCADATA equ $c03b
* Sound GLU
SGCONTROL equ $c03c ; Sound GLU control register
SGDATA equ $c03d ; Sound GLU data register
SGADDRL equ $c03e ; Sound GLU address reg low
SGADDRH equ $c03f ; Sound GLU address reg high
* interrupt vectors. JMP ABSLONG.
IRQ1SEC equ $e10054
IRQMOUSE equ $e10034
IRQQTR equ $e10038
IRQVBL equ $e10030
IRQSND equ $e1002c
IRQSCAN equ $e10028
IRQSERIAL equ $e10024
IRQATALK equ $e10020

8
debug.S Normal file
View File

@ -0,0 +1,8 @@
debug mac
brl eom
dw $7771
str ']1'
eom
<<<

6
make-vt100.sh Normal file
View File

@ -0,0 +1,6 @@
iix qlink vt100.link.S && \
profuse -orw vt100.po && \
cp vt100.system /Volumes/vt100/vt100.system \
&& umount /Volumes/vt100

198
screen.S Normal file
View File

@ -0,0 +1,198 @@
rel
mx %11
ent erase_line
ent erase_line_0
ent erase_line_1
ent erase_line_2
erase_line
; a = 0 - erase x - eol (inclusive)
; a = 1 - erase 0 - x (inclusive)
; a = 2 - erase 0 - eol
cmp #2
bcs :rts
asl
tax
jmp (:table,x)
:rts rts
:table
dw erase_line_0
dw erase_line_1
dw erase_line_2
erase_line_2
* erase entire line.
php
rep #$30
ldy #38
lda #" "
:loop
sta [text01],y
sta (text00),y
dey
dey
bpl :loop
plp
rts
mx %11
erase_line_0
* erase to eol
lda x
lsr
tay
lda #" "
bcs :half
:loop
sta [text01],y
:half sta (text00),y
iny
cpy #40
bcc :loop
rts
erase_line_1
* erase to x (inclusive)
lda x
lsr
tay
lda #" "
bcc :half
:loop
sta (text00),y
:half sta [text01],y
dey
bpl :loop
rts
erase_screen ent
; a = 0 - erase [cursor, end] (inclusive)
; a = 1 - erase [start, cursor] (inclusive)
; a = 2 - erase [start, end]
cmp #2
bcs :rts
asl
tax
jmp (:table,x)
:rts rts
:table
dw erase_screen_0
dw erase_screen_1
dw erase_screen_2
erase_screen_2 ent
* erase the entire screen.
php
rep #$30
lda #" " ; high bit set.
c00 cc $0400
c01 cc $0480
c02 cc $0500
c03 cc $0580
c04 cc $0600
c05 cc $0680
c06 cc $0700
c07 cc $0780
c08 cc $0428
c09 cc $04a8
c10 cc $0528
c11 cc $05a8
c12 cc $0628
c13 cc $06a8
c14 cc $0728
c15 cc $07a8
c16 cc $0450
c17 cc $04d0
c18 cc $0550
c19 cc $05d0
c20 cc $0650
c21 cc $06d0
c22 cc $0750
c23 cc $07d0
plp
rts
mx %11
erase_screen_0 ent
* erase from cursor to the end.
mx %11
ldx #0 ; for jmp (,x)
lda x
ora y
beq :all
lda x
beq :x0
jsr erase_line_0
lda y
inc
bra :x1
:x0
lda y
:x1 cmp #23
bcs :rts
asl
tax
:all php ; clear_table will plp.
rep #$30
lda #" "
jmp (clear_table,x)
:rts rts
erase_screen_1 ent
* erase from start to cursor.
jsr erase_line_1
lda y
bne :ok
rts
:ok
php
rep #$30
lda y
dey
asl
tax
lda #" "
jmp (:clear_table,x)
:clear_table
c23 cc $07d0
c22 cc $0750
c21 cc $06d0
c20 cc $0650
c19 cc $05d0
c18 cc $0550
c17 cc $04d0
c16 cc $0450
c15 cc $07a8
c14 cc $0728
c13 cc $06a8
c12 cc $0628
c11 cc $05a8
c10 cc $0528
c09 cc $04a8
c08 cc $0428
c07 cc $0780
c06 cc $0700
c05 cc $0680
c04 cc $0600
c03 cc $0580
c02 cc $0500
c01 cc $0480
c00 cc $0400
plp
rts

95
table.py Normal file
View File

@ -0,0 +1,95 @@
import sys
special = {
}
for x in range(0,0x20): special[chr(x)] = '^' + chr(0x40 + x)
cmap = {
'\'': '\'',
'\"': '\"',
'?': '?',
'\\': '\\',
'a': '\a',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
'v': '\v',
}
argv = sys.argv[1:]
chars = []
for arg in argv:
if arg == "--ctrl":
for c in range(0,32):
chars.append(chr(c))
continue
if arg == "--upper":
for c in range(ord('A'), ord('Z')+1):
chars.append(chr(c))
continue
if arg == "--lower":
for c in range(ord('a'), ord('z')+1):
chars.append(chr(c))
continue
if arg == "--digit":
for c in range(ord('0'), ord('9')+1):
chars.append(chr(c))
continue
# ^X is a control character
if len(arg) == 2 and arg[0] == '^':
c = chr(ord(arg[1]) & 0x1f)
chars.append(c)
continue
# \X is an escaped character
if len(arg) == 2 and arg[0] == '\\':
c = arg[1]
if c in cmap: chars.append(cmap[c])
continue
# X-Y is a range of characters.
if len(arg) == 4 and arg[1] == '-':
a = arg[0]
b = arg[2]
for c in range(ord(a),ord(b)+1):
chars.append(chr(c))
continue
chars.extend(arg)
chars = list(set(chars))
chars.sort()
if not chars: exit(1)
mmin = ord(chars[0])
mmax = ord(chars[-1])
print(":MIN\tequ {}".format(mmin))
print(":MAX\tequ {}".format(mmax))
print()
print(":table")
for x in range(mmin, mmax+1):
c = chr(x)
print("\tdw $0\t; {}".format(special.get(c, c)))
# for c in chars:
# x = ord(c)
# print("\tdw $0\t; {}".format(special.get(c, c)))

100
vt.equ.S Normal file
View File

@ -0,0 +1,100 @@
cas se
st_vt52 equ 0
st_vt52_esc equ 2
st_vt52_dca equ 4
st_vt100 equ 6
st_vt100_esc equ 8
st_vt100_csi equ 10
st_vt100_csi_2 equ 12
st_vt100_esc_pound equ 14 ; #
st_vt100_esc_lparen equ 16 ; (
st_vt100_esc_rparen equ 18 ; )
st_vt100_esc_bad equ 20
st_vt100_csi_bad equ 22
ESC equ $1b
DPAGE equ $1f00
dum 0
state ds 2
x ds 2
y ds 2
DECTM ds 2 ; top margin
DECBM ds 2 ; bottom margin
LOCAL ds 2 ; local mode
DECANM ds 2 ; ansi/vt52
DECKPAM ds 2 ; alternate keypad
DECCKM ds 2 ; cursor key modes
DECOM ds 2 ; origin
DECSCNM ds 2 ; screen mode
DECAWM ds 2 ; wrap
DECARM ds 2 ; auto repeat
DECCOLM ds 2 ; character per line (80/132)
LNM ds 2 ; new line
SGR ds 2 ; graphics, bit 1 = bold, 4 = underscore, 5 = blink, 7 = inverse
* not supported
*CHARSET ds 2 ;
*GRAPHICS ds 2 ;
*DECINLM ds 2 ; interlace
*DECSCLM ds 2 ; scroll mode
* DECDHL - double height line
* DECDWL - double width line
* parameters
MAX_PCOUNT equ 8
pcount ds 2
parms ds MAX_PCOUNT
pmod ds 2
; scratch registers
r0 ds 2
cursor_saved_char ds 2 ; saved char under the cursor
cursor_base ds 4
cursor_offset ds 2
cursor_char ds 2 ; cursor character
cursor_state ds 2 ; on/off/disabled.
draw_inverse ds 2 ; flag to draw inverse
erase_char ds 2 ; clear character
* keypress data.
key ds 2
mod ds 2
* saved cursor
saved_x ds 2
saved_y ds 2
saved_decom ds 2
saved_sgr ds 2
* async read/write pointers.
*
read_q_head ds 2
read_q_tail ds 2
write_q_head ds 2
write_q_tail ds 2
do *>256
err "too big"
fin
dend

177
vt100.S Normal file
View File

@ -0,0 +1,177 @@
vt100
mx %11
and #$7f
cmp #' '
bcs :notctrl
asl
tax
jmp (ctrl,x)
:notctrl
ldx state
jmp (:state_table,x)
:state_table
ext vt52_esc,vt52_dca
ext vt100_esc,vt100_csi,vt100_csi_2
ext vt100_esc_pound,vt100_esc_lparen,vt100_esc_rparen
dw char
dw vt52_esc
dw vt52_dca
dw char
dw vt100_esc
dw vt100_csi
dw vt100_csi_2
dw vt100_esc_pound
dw vt100_esc_lparen
dw vt100_esc_rparen
vt100_csi_bad ent
cmp #'@'
blt :rts
ldx #st_vt100
stx state
:rts rts
esc_csi
stz pcount
stz params
stz params+1
stz csi_private
lda #st_csi
sta state
rts
csi
inc state
inc state
cmp #'?'
bne csi0
lda #$80
sta csi_private
rts
csi0
cmp #';'
beq :semi
cmp #'0'
bcc csi_final
cmp #'9'+1
bcs csi_final
:num
and #$0f ; 0-9
tay ; save
ldx pcount
lda params,x
asl ; x 2
sta params,x
asl ; x 4
asl ; x 8
clc
adc params,x
sta params,x
tya
clc
adc params,x
sta params,x
rts
:semi
ldx pcount
inx
cpx #MAX_PCOUNT
bcs :srts
stx pcount
stz params,x
:srts
rts
csi_final
* c, h, l have private modes.
bit csi_private
bmi :priv
cmp #:MIN_FINAL
bcc :rts
cmp #:MAX_FINAL+1
bcs :rts
asl
tax
jmp (:table,x)
:rts rts
:priv
cmp #:MIN_FINAL_PRIV
bcc :rts
cmp #:MAX_FINAL_PRIV+1
bcs :rts
asl
tax
jmp (:table_priv,x)
csi
*
* ESC [ encountered.
*
* ? -> DEC private
* 0-9 -> parameter value
* ; parameter delim
* other - final character.
cmp #'0'
blt :notnum
cmp #'9'+1
bge :notnum
sep #$30
and #$0f
tay
ldx pcount
lda parms,x
asl
pha
asl
asl
clc
adc 1,s
sta 1,s
tya
adc 1,s
ply
sta parms,x
sep #$30
]rts rts
:notnum
cmp #';'
bne :notsemi
ldx pcount
cpx #MAXPCOUNT
bge ]rts
inx
stx pcount
stz parms,
stz parms+1,x
]rts rts
:notsemi
cmp #'?'
bne :final
sta ptype
rts
:final

199
vt100.beep.S Normal file
View File

@ -0,0 +1,199 @@
lst off
cas se
rel
xc
xc
use apple2gs.equ
mx %11
*
*
* keypress @ 44.1 - 28 samples of $40, 29 samples of $b0
*
* beep - repeat keypress 53 times
*
* by adjusting rate, only need 1 sample each.
*
* w/ 2 oscillators, scan rate = 223.7 kHz ~5x 44.1
* 28 samples * 5 = 142
* res = 7. (1 << 16) / 142 = ~ 461
* freq = 461, res = 7, wave size = 256 --> n * 461 >> 16
*
*
*
* vt52
* keypress - ~730 samples, $40, curve to 0 or $b0, curve to 0
* beep 14 keypresses
*
*
bic mac
if #=]1
and ]1!$ffff
else
err 1 ; immediate only.
fin
<<<
docwait mac
if MX<2
err 1 ; 8-bit m only
fin
wait lda SGCONTROL
bmi wait
<<<
docmode mac
docwait
bic #%01000000 ; DOC mode
ora #%00100000 ; auto-incr
ora #%0000_1111 ; max volume.
sta SGCONTROL
<<<
rammode mac
docwait
ora #%01100000 ; RAM mode, auto-incr
sta SGCONTROL
<<<
init_audio ent
docmode
* 4 oscillators. -- (2 for key, 2 for beep -- stereo!)
lda #$e1
sta SGADDRL
lda #4*2
sta SGDATA
* control registers
* just set them off. channels will be set when they fire.
lda #$a0
sta SGADDRL
lda #%0000_0_01_1 ; ch 0, interrupts off, 1-shot, halted.
sta SGDATA
sta SGDATA
sta SGDATA
sta SGDATA
* freq low
lda #$00
sta SGADDRL
lda #<650
sta SGDATA
sta SGDATA
sta SGDATA
sta SGDATA
* freq high
lda #$20
sta SGADDRL
lda #>650
sta SGDATA
sta SGDATA
sta SGDATA
sta SGDATA
* volume
lda #$40
sta SGADDRL
lda #$ff
sta SGDATA
sta SGDATA
sta SGDATA
sta SGDATA
* wave table
lda #$80
sta SGADDRL
lda #0 ; page 0
sta SGDATA
sta SGDATA
inc ; page 1
sta SGDATA
sta SGDATA
* wave table size
lda #$c0
sta SGADDRL
lda #%00_000_111 ; 256 bytes, 16-bit shift
sta SGDATA
sta SGDATA
sta SGDATA
sta SGDATA
; now copy the data....
rammode
stz SGADDRL
stz SGADDRH
ldy #key_size
ldx #0
]loop lda key_data,x
sta SGDATA
inx
dey
bpl ]loop
stz SGADDRL
lda #1
sta SGADDRH
ldy #beep_size
ldx #0
]loop lda beep_data,x
sta SGDATA
inx
dey
bpl ]loop
rts
beep ent
; fire osc 2 + 3
docmode
lda #$a0+2
sta SGADDRL
lda #%0000_0_01_0 ; ch 0, interrupts off, 1-shot, running.
ldx #%0001_0_01_0 ; ch 1, interrupts off, 1-shot, running.
sta SGDATA
stx SGDATA
rts
key_data
db $01
db $ff
db $00
key_size equ *-key_data
beep_data
lup 53
db $01
db $ff
--^
db $0
beep_size equ *-beep_data
sav vt100.beep.L

1048
vt100.cda.S Normal file

File diff suppressed because it is too large Load Diff

899
vt100.csi.S Normal file
View File

@ -0,0 +1,899 @@
lst off
cas se
rel
xc
xc
use vt.equ
use debug
mx %11
ext reset_tab,reset_all_tabs
ext recalc_cursor,recalc_cursor_x,recalc_cursor_y
ext erase_line_0,erase_line_1,erase_line_2
ext erase_screen_0,erase_screen_1,erase_screen_2
ext update_sgr
ext write_modem,write_modem_str
vt100_csi ent
debug vt100_csi
* 0123456789;ycnlhgrqJKmABCDHf
* based on testing -
* everything except '0' - '?' and control chars
* will finish.
* '?' only matters for h/l
* a misplaced ? (or anything in '0' - '?', except 0-9;)
* will cancel the sequence AFTER it's finished.
* < = > ? are allowed as an initial modifier but only '?' is private
* a mis-placed < = > ? will prevent 0x20-0x2f from terminating the sequence.
ldx #st_vt100
stx state
stz pcount
stz parms
stz parms+1 ; some assume 2 parms.
stz pmod
* tay ; save for modifier
cmp #:MIN
blt :rts
cmp #:MAX+1
bge :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
*
:rts rts
:MIN equ 48
:MAX equ 121
:table
dw :digit ; 0
dw :digit ; 1
dw :digit ; 2
dw :digit ; 3
dw :digit ; 4
dw :digit ; 5
dw :digit ; 6
dw :digit ; 7
dw :digit ; 8
dw :digit ; 9
dw digit ; :
dw semi
dw :xmod ; <
dw :xmod ; =
dw :xmod ; >
dw :modifier ; ?
dw :rts ; @
dw csi_A ; A
dw csi_B ; B
dw csi_C ; C
dw csi_D ; D
dw :rts ; E
dw :rts ; F
dw :rts ; G
dw csi_H ; H
dw :rts ; I
dw csi_J ; J
dw csi_K ; K
dw :rts ; L
dw :rts ; M
dw :rts ; N
dw :rts ; O
dw :rts ; P
dw :rts ; Q
dw :rts ; R
dw :rts ; S
dw :rts ; T
dw :rts ; U
dw :rts ; V
dw :rts ; W
dw :rts ; X
dw :rts ; Y
dw :rts ; Z
dw :rts ; [
dw :rts ; \
dw :rts ; ]
dw :rts ; ^
dw :rts ; _
dw :rts ; `
dw :rts ; a
dw :rts ; b
dw csi_c ; c
dw :rts ; d
dw :rts ; e
dw csi_f ; f
dw csi_g ; g
dw csi_h ; h
dw :rts ; i
dw :rts ; j
dw :rts ; k
dw csi_l ; l
dw csi_m ; m
dw csi_n ; n
dw :rts ; o
dw :rts ; p
dw csi_q ; q
dw csi_r ; r
dw :rts ; s
dw :rts ; t
dw :rts ; u
dw :rts ; v
dw :rts ; w
dw :rts ; x
dw csi_y ; y
:digit
ldx #st_vt100_csi_2
stx state
lsr ; undo asl
sta parms
rts
:modifier
ldx #st_vt100_csi_2
stx state
lda #$80
sta pmod
rts
:xmod
* ignored.
ldx #st_vt100_csi_2
stx state
rts
vt100_csi_bad ent
cmp #'@'
blt :rts
ldx #st_vt100
stx state
:rts rts
vt100_csi_2 ent
debug vt100_csi_2
ldx #st_vt100
stx state
cmp #:MIN
blt :rts
cmp #:MAX+1
bge :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:rts rts
:MIN equ 48
:MAX equ 121
:table
dw digit ; 0
dw digit ; 1
dw digit ; 2
dw digit ; 3
dw digit ; 4
dw digit ; 5
dw digit ; 6
dw digit ; 7
dw digit ; 8
dw digit ; 9
dw digit ; :
dw semi
dw :modifier ; <
dw :modifier ; =
dw :modifier ; >
dw :modifier ; ?
dw :rts ; @
dw csi_A ; A
dw csi_B ; B
dw csi_C ; C
dw csi_D ; D
dw :rts ; E
dw :rts ; F
dw :rts ; G
dw csi_H ; H
dw :rts ; I
dw csi_J ; J
dw csi_K ; K
dw :rts ; L
dw :rts ; M
dw :rts ; N
dw :rts ; O
dw :rts ; P
dw :rts ; Q
dw :rts ; R
dw :rts ; S
dw :rts ; T
dw :rts ; U
dw :rts ; V
dw :rts ; W
dw :rts ; X
dw :rts ; Y
dw :rts ; Z
dw :rts ; [
dw :rts ; \
dw :rts ; ]
dw :rts ; ^
dw :rts ; _
dw :rts ; `
dw :rts ; a
dw :rts ; b
dw csi_c ; c
dw :rts ; d
dw :rts ; e
dw csi_f ; f
dw csi_g ; g
dw csi_h ; h
dw :rts ; i
dw :rts ; j
dw :rts ; k
dw csi_l ; l
dw csi_m ; m
dw csi_n ; n
dw :rts ; o
dw :rts ; p
dw csi_q ; q
dw csi_r ; r
dw :rts ; s
dw :rts ; t
dw :rts ; u
dw :rts ; v
dw :rts ; w
dw csi_x ; x
dw csi_y ; y
:modifier
ldx #st_vt100_csi_bad
stx state
rts
semi
ldx #st_vt100_csi_2
stx state
ldx pcount
cpx #MAX_PCOUNT
bge :big
inx
stx pcount
:big stz parms,x
:rts rts
* parameter digit. clamped to 255 (250+ rounds up to 255)
* in 132 is the largest valid parameter so this is ok.
digit
ldx #st_vt100_csi_2
stx state
lsr ; undo asl
sta r0
ldx pcount
lda parms,x
* cmp #255
* beq :rts
cmp #25
bge :v
tay
lda :mult,y
* clc ; cleared via cmp
adc r0
sta parms,x
:rts
rts
:v
lda #$255
sta parms,x
rts
:mult db 0,10,20,30,40,50,60,70,80,90
db 100,110,120,130,140,150,160,170,180,190
db 200,210,220,230,240,250
csi_h
; esc [ ... h (vt100)
; esc [ ? ... h (private)
ldy #$80
bra mode_common
csi_l
; esc [ ... l (vt100)
; esc [ ? ... l (private)
ldy #0
mode_common
inc pcount
ldx #0
:loop lda parms,x
cmp #:MIN
blt :next
cmp #:MAX+1
bge :next
phx
asl
tax
jsr (:table,x)
plx
:next inx
cpx pcount
blt :loop
:rts rts
:MIN equ 0
:MAX equ 20
:table
dw :rts ; error
dw mode_DECCKM
dw mode_DECANM
dw mode_DECCOLM ; DECCOLM
dw :rts ; DECSCLM
dw mode_DECSCNM
dw mode_DECOM
dw mode_DECAWM
dw mode_DECARM
dw :rts ; DECINLM
dw :rts ; 10
dw :rts ; 11
dw :rts ; 12
dw :rts ; 13
dw :rts ; 14
dw :rts ; 15
dw :rts ; 16
dw :rts ; 17
dw :rts ; 18
dw :rts ; 19
dw mode_LNM
*:mask dw 0,$40,$40,$40,$40,$40,$40,$40,$40,$40
* dw 0,0,0,0,0,0,0,0,0,0,0
mode_DECCKM
bit pmod
bpl :rts
sty DECCKM
:rts rts
mode_DECANM
bit pmod
bpl :rts
sty DECANM
cpy #0
bne :rts
* switch to vt52 mode
ldx #st_vt52
stx state
:rts rts
mode_DECCOLM
* 80/132 mode.
* vt102 guide states:
* NOTE: When you change the number of columns per line, the screen is erased.
* This also sets the scrolling region for full screen (24 lines).
*
* based on testing, this always clears the screen and resets x/y, regardless of current mode.
*
bit pmod
bpl :rts
sty DECCOLM
lda #0
sta DECTM
lda #23
sta DECBM
stz x
stz y
phy
jsr recalc_cursor
jsr erase_screen_2
ply
:rts rts
mode_DECSCNM
bit pmod
bpl :rts
* todo - invert on-screen characters?
sty DECSCNM
:rts rts
mode_DECOM
bit pmod
bpl :rts
sty DECOM
; move to the new home position
stz x
stz y
cpy #0
beq :rts
lda DECTM
sta y
phy
jsr recalc_cursor
ply
:rts rts
mode_DECAWM
bit pmod
bpl :rts
sty DECAWM
:rts rts
mode_DECARM
bit pmod
bpl :rts
sty DECARM
:rts rts
mode_LNM
bit pmod
bmi :rts
sty LNM
:rts rts
csi_m
* esc [ ... m
* 0 - attributes off
* 1 - bold
* 4 - underscore
* 5 - blink
* 7 - inverted
inc pcount
ldx #0
:loop lda parms,x
cmp #8
bge :next
tay
lda SGR
and :and,y
ora :or,y
sta SGR
:next inx
cpx pcount
blt :loop
jmp update_sgr
:and db $00,$ff,$ff,$ff,$ff,$ff,$ff,$ff
:or db %0000_0000,%0000_0010,%0000_0000,%0000_0000
db %0001_0000,%0010_0000,%0000_0000,%1000_0000
csi_g
* ESC [ g, ESC [ 0 g - clear tab at column
* ESC [ 3 g - clear all tabs
lda parms
beq :0
cmp #3
beq :3
rts
:0 ldx x
jmp reset_tab
:3 jmp reset_all_tabs
p1 mac
lda parms
bne ok
lda #1
ok sta parms
<<<
* cursor movement.
* if private mode, no effect.
csi_A
* up
* if cursor is outside the scrolling region, it is not locked to the scrolling region.
bit pmod
bmi :rts
p1
lda y
cmp DECTM
beq :rts
bcc :simple
sec
sbc parms
bcc :top
cmp DECTM
* bcc :top
* bra :sta
bcs :sta
:top lda DECTM
bra :sta
:rts rts
:simple
* lda y
sec
sbc parms
* bcc :0 ; clear indicate underflow.
bcc :sta
:0 lda #0
:sta sta y
jmp recalc_cursor_y
csi_B
* down
bit pmod
bmi :rts
p1
lda y
cmp DECBM
beq :rts
bge :simple
clc
adc parms
bcs :bottom ; overflow
cmp DECBM
bcc :sta
:bottom lda DECBM
bra :sta
:rts rts
:simple
clc
adc parms
bcs :23 ; overflow
cmp #24
bcc :sta
:23 lda #23
:sta sta y
jmp recalc_cursor_y
csi_C
* right
* in column 80, no effect.
bit pmod
bmi :rts
lda x
cmp #79
bcs :rts
p1
lda x
* and #$7f
clc
adc parms
bcs :79 ; overflow
cmp #80
bcc :sta
:79 lda #79
:sta sta x
jmp recalc_cursor_x
:rts rts
csi_D
* left
bit pmod
bmi :rts
p1
lda x
and #$7f
sec
sbc parms
* bcc :0 ; underflow
bcs :sta
:0 lda #0
:sta sta x
jmp recalc_cursor_x
:rts rts
csi_f
csi_H ; direct cursor addressing
debug csi_H
* honors origin
* large numbers are clamped
* 0 or 1 treated as 1 (1-based counting)
* based on testing, esc [ 253-255 H will position outside the scrolling
* region when DECOM is active (to first 3 lines, respectively)
* this is not emulated.
* y
lda parms
beq :yy
dec
:yy bit DECOM
bmi :org
cmp #23
blt :yyy
lda #23
:yyy sta y
bra :x
:org
clc
adc DECTM
cmp DECBM
blt :org1
lda DECBM
:org1 sta y
* x
:x
ldx parms+1
beq :xx
dex
:xx
cpx #79
blt :xxx
ldx #79
:xxx stx x
jmp recalc_cursor
csi_r ; scrolling region
debug csi_r
* based on testing
* esc [ n r (no second parmeter) is equivalent to esc [ n ; 24 r
* esc [ r sets scrolling region to 1 ; 24 ( in accordance with above )
* 24 is assumed value for second parameter
* invalid parameters exit without updating
* based on testing, row parameters are not affected by DECOM.
lda parms
beq :p1
dec parms
:p1
lda parms+1
beq :p2
dec parms+1
bra :check
:p2 lda #23
sta parms+1
:check
* 23 max
ldx parms+0
cpx #23+1
bge :rts
ldx parms+1
cpx #23+1
bge :rts
* must be at least 1 line
lda parms+1
sec
sbc parms
beq :rts
bmi :rts
* move cursor to origin.
lda parms
sta DECTM
sta y
lda parms+1
sta DECBM
stz x
bit DECOM
bmi :j
stz y
:j jmp recalc_cursor
:rts rts
csi_J ; erase screen
lda parms
cmp #2+1
bcs :rts
asl
tax
jmp (:table,x)
:rts rts
:table
dw erase_screen_0
dw erase_screen_1
dw erase_screen_2
csi_K ; erase line
lda parms
cmp #2+1
bcs :rts
asl
tax
jmp (:table,x)
:rts rts
:table
dw erase_line_0
dw erase_line_1
dw erase_line_2
csi_q ; LEDs
rts
csi_n ; status report
bit LOCAL
bmi :rts
lda parms
cmp #5
beq :dsr
cmp #6
beq :cpr
:rts rts
:dsr ; report status
lda #ESC
jsr write_modem
lda #'['
jsr write_modem
lda #'0'
jsr write_modem
lda #'n'
jmp write_modem
:cpr ; cursor report
* returned y is in terms of DECOM.
lda #ESC
jsr write_modem
lda #'['
jsr write_modem
lda y
bit DECOM
bpl :y
sec
sbc DECTM
:y inc
jsr write_digit
lda #';'
jsr write_modem
lda x
inc
jsr write_digit
lda #'R'
jmp write_modem
write_digit
* digit must be in the range 1-80
cmp #10
bcs :multi
:0 ora #'0'
jmp write_modem
:multi
ldx #8
]loop cmp :table,x
bcs :ok
dex
bra ]loop
:ok sec
sbc :table,x
pha ; save
txa
ora #'0'
jsr write_modem
pla
bra :0
:table db 0,10,20,30,40,50,60,70,80
csi_c ; what are you?
* DA - Device Attributes
mx %11
php
rep #$10 ; long x/y
ldy #:response
jsr write_modem_str
plp
rts
:response asc 1b,'[?1;0c',00 ; No options.
csi_y ; invoke confidence test
* ???
rts
csi_x ; request terminal parameters
* DECREQTPARM Request Terminal Parameters
mx %11
lda parms
cmp #2
bcs :rts
inc
inc
ora #'0'
sta :response+2
php
rep #$10 ; long x/y
ldy #:response
jsr write_modem_str
plp
:rts rts
:response asc 1b,'[x;1;1;112;112;1;0x',00 ; no parity, 8-bits, 9600/9600, 16x multiplier, no stp flags.
sav vt100.csi.L

122
vt100.ctrl.S Normal file
View File

@ -0,0 +1,122 @@
lst off
cas se
rel
xc
xc
use vt.equ
*control chars
ext draw_char,update_cursor
control ent
asl
tax
jmp (:table,x)
:table
dw :rts ; ^@
dw :rts ; ^A
dw :rts ; ^B
dw :rts ; ^C
dw :rts ; ^D
dw enq ; ^E
dw :rts ; ^F
dw bel ; ^G
dw bs ; ^H
dw tab ; ^I
dw lf ; ^J
dw vt ; ^K
dw ff ; ^L
dw cr ; ^M
dw so ; ^N
dw si ; ^O
dw :rts ; ^P
dw xon ; ^Q
dw :rts ; ^R
dw xoff ; ^S
dw :rts ; ^T
dw :rts ; ^U
dw :rts ; ^V
dw :rts ; ^W
dw can ; ^X
dw :rts ; ^Y
dw sub ; ^Z
dw esc ; ^[
dw :rts ; ^\
dw :rts ; ^]
dw :rts ; ^^
dw :rts ; ^_
:rts rts
enq
* send answer back message.
* answer back message is a user-controllable string of text sent as-is
* (with no specific terminator character)
rts
bel
* todo - trigger nice ensoniq beep.
rts
bs
* backspace, no wrap
lda x
beq :rts
and #$7f
dec
sta x
jmp update_cursor
:rts rts
tab
* go to next tab stop
* tab at 80 does not reset overflow.
rts
lf
vt
ff
* vt and ff interpreted as lf
* LNM: also do cr.
bit LNM
bpl :lnm
stz x
:lnm
lda y
cmp BM ; bottom margin
bne :iny
jmp scroll_up
:iny inc y
jmp update_cursor
cr
stz x
jmp update_cursor
so ; G1 character set
si ; G0 character set
rts
xon
xoff
* flow control...
rts
can
sub
ext draw_char_raw
* cancel esc sequence and display error character
stz state
lda #$57 ; mouse text block
jmp draw_char_raw
rts
esc
lda #st_esc
sta state
rts

320
vt100.esc.S Normal file
View File

@ -0,0 +1,320 @@
lst off
rel
xc
xc
use vt.equ
mx %11
ext recalc_cursor,recalc_cursor_x,recalc_cursor_y
ext scroll_up,scroll_down
ext reset
ext update_sgr
vt100_esc ent
* #[()=>cH78DEM
* based on testing, unspecified chars in the 0x20-0x2f range cause it
* to gobble chars until 0x30- terminator (which ends the sequence but
* does not take an action)
* esc 1 -> hangs? [undocumented]
ldx #st_vt100
stx state
cmp #:MIN
blt :bad
cmp #:MAX+1
bge :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:bad
ldx #st_vt100_esc_bad
stx state
:rts
rts
:MIN equ 35
:MAX equ 99
:table
dw :pound ; #
dw :bad ; $
dw :bad ; %
dw :bad ; &
dw :bad ; '
dw :lparen ; (
dw :rparen ; )
dw :bad ; *
dw :bad ; +
dw :bad ; ,
dw :bad ; -
dw :bad ; .
dw :bad ; /
dw :rts ; 0
dw :rts ; 1
dw :rts ; 2
dw :rts ; 3
dw :rts ; 4
dw :rts ; 5
dw :rts ; 6
dw esc_7 ; 7
dw esc_8 ; 8
dw :rts ; 9
dw :rts ; :
dw :rts ; ;
dw :rts ; <
dw esc_eq ; =
dw esc_gt ; >
dw :rts ; ?
dw :rts ; @
dw :rts ; A
dw :rts ; B
dw :rts ; C
dw esc_D ; D
dw esc_E ; E
dw :rts ; F
dw :rts ; G
dw esc_H ; H
dw :rts ; I
dw :rts ; J
dw :rts ; K
dw :rts ; L
dw esc_M ; M
dw :rts ; N
dw :rts ; O
dw :rts ; P
dw :rts ; Q
dw :rts ; R
dw :rts ; S
dw :rts ; T
dw :rts ; U
dw :rts ; V
dw :rts ; W
dw :rts ; X
dw :rts ; Y
dw :rts ; Z
dw :csi ; [
dw :rts ; \
dw :rts ; ]
dw :rts ; ^
dw :rts ; _
dw :rts ; `
dw :rts ; a
dw :rts ; b
dw esc_c ; c
:lparen
ldx #st_vt100_esc_lparen
stx state
rts
:rparen
ldx #st_vt100_esc_rparen
stx state
rts
:pound
ldx #st_vt100_esc_pound
stx state
rts
:csi
ldx #st_vt100_csi
stx state
rts
esc_7 ; save cursor position, graphic rendition, and character set.
* based on testing, DECOM is also saved/restored.
lda x
sta saved_x
lda y
sta saved_y
lda DECOM
sta saved_decom
lda SGR
sta saved_sgr
rts
esc_8 ; restore cursor position, graphic rendition, and character set.
lda saved_x
sta x
lda saved_y
sta y
lda saved_decom
sta DECOM
lda saved_sgr
sta SGR
jsr update_sgr
jmp recalc_cursor
esc_eq ; enter alternate keypad mode
lda #$80
sta DECKPAM
rts
esc_gt ; exit alternate keypad mode
stz DECKPAM
rts
esc_H ; set tab stop
ext set_tab
ldx x
bmi :rts
jmp set_tab
:rts rts
esc_E ; next line
* This sequence causes the active position to move to the first position
* on the next line downward. If the active position is at the bottom
* margin, a scroll up is performed.
stz x
jsr recalc_cursor_x
; drop through
esc_D ; index
* This sequence causes the active position to move downward one line
* without changing the column position. If the active position is at the
* bottom margin, a scroll up is performed.
lda y
cmp DECBM
beq :scroll
cmp #23
beq :rts
inc y
jmp recalc_cursor_y
:scroll jmp scroll_down
:rts rts
esc_M ; reverse index
* Move the active position to the same horizontal position on the
* preceding line. If the active position is at the top margin, a scroll
* down is performed.
lda y
cmp DECTM
beq :scroll
cmp #0
beq :rts
dec y
jmp recalc_cursor_y
:scroll jmp scroll_up
:rts rts
esc_c ; TODO - reset terminal.
jmp reset
vt100_esc_bad ent
cmp #'0'
blt :rts
ldx #st_vt100
stx state
:rts
rts
vt100_esc_pound ent
* esc # 3 - make line double height (top half)
* esc # 4 - make line double height (bottom half)
* esc # 5 - make line single width, single height
* esc # 6 - make line double width
* esc # 8 - screen alignment - fill screen with E (SGR not honored)
* based on testing, this also resets the scrolling region and homes
* the cursor.
* based on testing, 0+ are term characters, 0x20-0x2f puts it in
* esc_bad state
ldx #st_vt100
stx state
cmp #:MIN
blt :bad
cmp #:MAX+1
bge :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:bad ldx #st_vt100_esc_bad
stx state
:rts rts
:MIN equ 48
:MAX equ 57
:table
dw :rts ; 0
dw :rts ; 1
dw :rts ; 2
dw :rts ; 3
dw :rts ; 4
dw :rts ; 5
dw :rts ; 6
dw :rts ; 7
dw :e ; 8
dw :rts ; 9
:e
* TODO - does this reset DECOM?
ext fill_screen
stz x
stz y
stz DECTM
lda #23
sta DECBM
jsr recalc_cursor
lda #"E"
jmp fill_screen
vt100_esc_lparen ent
vt100_esc_rparen ent
* ( sets G0, ) sets G1
* A - UK set
* B - ASCII set
* 0 - Special Graphics
* 1 - Alternate Char ROM Standard Char Set
* 2 - Alternate Char ROM Special Graphics
* SO, aka Control-N aka 0x0e set the G1 char set
* SI, aka Control-O aka 0x0f set the G0 char set
* not currently supported.
* TODO - mouse text support?
ldx #st_vt100
stx state
cmp #'0'
blt :bad
rts
:bad ldx #st_vt100_esc_bad
stx state
:rts rts
sav vt100.esc.L

465
vt100.key.S Normal file
View File

@ -0,0 +1,465 @@
lst off
rel
xc
xc
tbx on
use vt.equ
use apple2gs.equ
use debug
mx %11
* ext dispatch
ext write_modem
kmShift equ %0000_0001
kmControl equ %0000_0010
kmCapsLock equ %0000_0100
kmRepeat equ %0000_1000
kmKeypad equ %0001_0000
kmUpdateMod equ %0010_0000
kmOption equ %0100_0000
kmCommand equ %1000_0000
*
* The vt100 has a delete key and a backspace key.
* delete sends 0x7f. backspace sends 0x08.
* stty is general set so 0x7f is the erase character.
* termcaps generally claim 0x08 is the backspace character.
*
* emacs, by default, thinks 0x08 ( ^H ) means you want help.
*
* so, backspace will send 0x7f. control-H or command-backspace
* will send 0x08.
*
* TODO - keys
* command-L -> local/online mode?
* command-Q -> quit
* command-K -> clear screen?
* command-R -> reset settings
*
dispatch
jmp write_modem
keypress ent
debug keypress
lda KBD
bmi :key
:rts rts
:key
and #$7f
sta key
lda KEYMOD
sta mod
sta KEYSTROBE
* if DECARM is clear, skip repeat characters.
*
* a REAL vt100 will never auto-repeat ESC, TAB, RETURN, or if Control is also pressed.
*
bit DECARM
bpl :arm
bit #kmRepeat
bne :rts
:arm
bit #kmOption!kmCommand
bne command
bit #kmKeypad
jne keypad
bit #kmControl
bne :ctrl
lda key
cmp #' '
bcs :notctrl
* control char w/o control bit.
* ie, arrow key / return / tab
* no cmp / sbc needed
asl
tax
lsr ; restore
jmp (special,x)
:ctrl
lda key
and #$1f ; control-space should generate 0, not $20.
bra :send
:notctrl
* cmp #$7f ; delete - special case
* bne :send
* lda #$08
:send jmp dispatch
command ; or option
* apple-return -> linefeed
* apple-backspace -> delete
lda key
cmp #$7f
beq :bs
cmp #$0d
beq :lf
cmp #'a'
bcc :0
cmp #'z'+1
bcs :0
and #$df ; ~ $20
:0
cmp #:MIN
blt :rts
cmp #:MAX+1
bcs :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:rts rts
:bs lda #$08
jmp dispatch ;
:lf lda #$0a
jmp dispatch
ext enable_modem,disable_modem
:local
bit LOCAL
bmi :online
lda #$80
sta LOCAL
jmp disable_modem
:online
stz LOCAL
jmp enable_modem
:quit
ext quit
jmp quit
rts
:reset
* TODO
rts
:clear
* TODO
rts
:MIN equ 49
:MAX equ 82
:table
dw pf1 ; 1
dw pf2 ; 2
dw pf3 ; 3
dw pf4 ; 4
dw :rts ; 5
dw :rts ; 6
dw :rts ; 7
dw :rts ; 8
dw :rts ; 9
dw :rts ; :
dw :rts ; ;
dw :rts ; <
dw :rts ; =
dw :rts ; >
dw :rts ; ?
dw :rts ; @
dw :rts ; A
dw :rts ; B
dw :rts ; C
dw :rts ; D
dw :rts ; E
dw :rts ; F
dw :rts ; G
dw :rts ; H
dw :rts ; I
dw :rts ; J
dw :clear ; K
dw :local ; L
dw :rts ; M
dw :rts ; N
dw :rts ; O
dw :rts ; P
dw :quit ; Q
dw :reset ; R
keypad
lda key
cmp #:MIN
blt :rts
cmp #:MAX+1
bcs :other
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:other
* keypad delete key ($75 aka 'u') will send as backspace ($08)
*
cmp #'u'
bne :rts
lda #$08
jmp dispatch
:rts rts
:MIN equ 13
:MAX equ 61
:table
dw enter ; ^M Enter -> \r, ESC ? M
dw :rts ; ^N
dw :rts ; ^O
dw :rts ; ^P
dw :rts ; ^Q
dw :rts ; ^R
dw :rts ; ^S
dw :rts ; ^T
dw :rts ; ^U
dw :rts ; ^V
dw :rts ; ^W
dw :rts ; ^X
dw :rts ; ^Y
dw :rts ; ^Z
dw pf1 ; ^[ PF1 -> ESC P
dw :rts ; ^\
dw :rts ; ^]
dw :rts ; ^^
dw :rts ; ^_
dw :rts ;
dw :rts ; !
dw :rts ; "
dw :rts ; #
dw :rts ; $
dw :rts ; %
dw :rts ; &
dw :rts ; '
dw :rts ; (
dw :rts ; )
dw pf4 ; *
dw comma ; +
dw :rts ; ,
dw dash ; -
dw dot ; .
dw pf3 ; / PF3 -> ESC R
dw digit ; 0
dw digit ; 1
dw digit ; 2
dw digit ; 3
dw digit ; 4
dw digit ; 5
dw digit ; 6
dw digit ; 7
dw digit ; 8
dw digit ; 9
dw :rts ; :
dw :rts ; ;
dw :rts ; <
dw pf2 ; = PF2 -> ESC Q
enter
bit DECKPAM
bmi :alt
brl cr
:alt
jmp ?O
comma
* iigs keyboard is a +
lda #','
sta key
bit DECKPAM
bmi :alt
jmp dispatch
:alt
jmp ?O
dot
lda key
bit DECKPAM
bmi :alt
jmp dispatch
:alt
jmp ?O
dash
lda key
bit DECKPAM
bmi :alt
jmp dispatch
:alt
jmp ?O
digit
lda key
bit DECKPAM
bmi :alt
jmp dispatch
:alt
* jmp ?O
* drop through
?O
* send ESC ? key if vt52, ESC O key if vt100
lda #ESC
jsr dispatch
bit DECANM
bpl :vt52
lda #'O'
jsr dispatch
lda key
ora #$40
jmp dispatch
:vt52
lda #'?'
jsr dispatch
lda key
ora #$40
jmp dispatch
pf1
lda #'P'
sta key
bra pf
pf2
lda #'Q'
sta key
bra pf
pf3
lda #'R'
sta key
bra pf
pf4
lda #'S'
sta key
pf
lda #ESC
jsr dispatch
bit DECANM
bpl :vt52
lda #'O'
jsr dispatch
:vt52 lda key
jmp dispatch
special
dw dispatch ; ^@
dw dispatch ; ^A
dw dispatch ; ^B
dw dispatch ; ^C
dw dispatch ; ^D
dw dispatch ; ^E
dw dispatch ; ^F
dw dispatch ; ^G
dw left ; ^H
dw dispatch ; ^I - tab
dw down ; ^J
dw up ; ^K
dw dispatch ; ^L
dw cr ; ^M
dw dispatch ; ^N
dw dispatch ; ^O
dw dispatch ; ^P
dw dispatch ; ^Q
dw dispatch ; ^R
dw dispatch ; ^S
dw dispatch ; ^T
dw right ; ^U
dw dispatch ; ^V
dw dispatch ; ^W
dw dispatch ; ^X
dw dispatch ; ^Y
dw dispatch ; ^Z
dw dispatch ; ^[
dw dispatch ; ^\
dw dispatch ; ^]
dw dispatch ; ^^
dw dispatch ; ^_
cr
* Return sends CR or CR + LF (LNM)
bit LNM
bmi :crlf
lda #$0d
jmp dispatch
:crlf
lda #$0d
jsr dispatch
lda #$0a
jmp dispatch
left
lda #'D'
bra arrow
right
lda #'C'
bra arrow
up
lda #'A'
bra arrow
down
lda #'B'
* drop through
arrow
* actual character generated depends on DECANM and DECCKM
sta key
lda #ESC
jsr dispatch
bit DECANM
bpl :vt52
bit DECCKM
bmi :cursor
lda #'['
jsr dispatch
lda key
jmp dispatch
:cursor
lda #'O'
jsr dispatch
* drop through.
:vt52
lda key
jmp dispatch
sav vt100.key.L

30
vt100.link.S Normal file
View File

@ -0,0 +1,30 @@
ovr all
* binary linker
lkv 0
org $2000
asm vt100.main.S
asm vt100.esc.S
asm vt100.tabs.S
asm vt100.vt52.S
asm vt100.csi.S
asm vt100.screen.S
asm vt100.modem.S
asm vt100.key.S
asm vt100.beep.S
asm vt100.cda.S
lnk vt100.main.L
lnk vt100.esc.L
lnk vt100.tabs.L
lnk vt100.vt52.L
lnk vt100.csi.L
lnk vt100.screen.L
lnk vt100.modem.L
lnk vt100.key.L
lnk vt100.beep.L
lnk vt100.cda.L
typ $ff
sav vt100.system

477
vt100.main.S Normal file
View File

@ -0,0 +1,477 @@
lst off
rel
xc
xc
tbx on ; qasm
mx %11
use vt.equ
use apple2gs.equ
use debug
ext scroll_down
ext recalc_cursor,recalc_cursor_x,recalc_cursor_y
ext modem_io,modem_vector,reset_modem_buffer
ext modem_startup,modem_shutdown
ext keypress
ext disable_cursor,enable_cursor,cursor_vector
ext erase_screen,fill_screen
ext init_tabs
ext init_audio
ext cda_startup,cda_shutdown
main debug main
clc
xce
cli
pea DPAGE
pld
jsr init
jsr enable_cursor
lda #4
tsb VGCINT ; enable 1-sec interrupt.
stz SCANINT ; reset 1-sec interrupt
loop
jsr keypress ; check for a keypress, write data to out buffer.
jsr modem_io ;
bcc :nope
pha
jsr disable_cursor
pla
jsr vt100
bra loop
:nope ; no modem data, re-enable the cursor.
jsr enable_cursor
bra loop
init
mx %11
lda #" "
jsr fill_screen ; erase first to prevent flash if going 40->80 columns.
sta TXTSET
sta SET80VID
sta SETALTCHAR
rep #$30
jsr init_mem
ldx #254
:zloop stz 0,x
dex
dex
bpl :zloop
lda #$0400
sta cursor_base
lda #$01
sta cursor_base+2
lda #" " ; 16-bit
sta erase_char
lda #$0080
sta cursor_state
sei
lda cursor_vector
stal IRQ1SEC
lda cursor_vector+2
stal IRQ1SEC+2
lda modem_vector
stal IRQSERIAL
lda modem_vector+2
stal IRQSERIAL+2
cli
lda #0 ; clear high byte
sep #$30
lda #"_"
sta cursor_char
lda #23
sta DECBM
lda #$80
* sta LOCAL
sta DECANM ; ANSI (vt100) on
sta DECARM ; key repeat on
* lda #st_vt52
lda #st_vt100
sta state
* jsr erase_screen
jsr modem_startup
jsr init_tabs
jsr init_audio
jsr cda_startup
rts
MasterID dw 0
init_mem
*
* see prodos technote #27
*
* _InstallCDA uses the memory manager ; otherwise I wouldn't bother
* This is here to prevent MM from stomping on our memory.
*
mx %00
stz MasterID
_TLStartUp
pha
_MMStartUp
pla
bcs :p8
rts
:p8
_MTStartUp
pea #0
pea #$1000
_GetNewID
pla
sta MasterID
* bank 0
pha
pha
pea #$0000
pea #$b800
lda MasterID
pha
pea #$c013
pea #0000
pea #0800
_NewHandle
pla
pla
* bank 1
pha
pha
pea #$0000
pea #$b800
lda MasterID
pha
pea #$c013
pea #$0001
pea #$0800
_NewHandle
pla
pla
rts
reset ent
mx %11
php
* disable 1-sec interrupt...
lda #4
trb VGCINT ; disable 1-sec interrupt.
stz SCANINT ; reset 1-sec interrupt
lda #" "
jsr fill_screen ; erase first to prevent flash if going 40->80 columns.
rep #$30
ldx #254
:zloop stz 0,x
dex
dex
bpl :zloop
lda #$0400
sta cursor_base
lda #$01
sta cursor_base+2
lda #" " ; 16-bit
sta erase_char
lda #$0080
sta cursor_state
lda #0 ; clear high byte
sep #$30
lda #"_"
sta cursor_char
lda #23
sta DECBM
lda #$80
sta DECANM ; ansi mode
sta DECARM ; key repeat on
lda #st_vt100
sta state
jsr init_tabs
* jsr enable_cursor
jsr reset_modem_buffer
lda #4
tsb VGCINT ; enable 1-sec interrupt.
stz SCANINT ; reset 1-sec interrupt
plp
rts
quit ent
* need to disable modem interrupts
sep #$30
lda #4
trb VGCINT ; disable 1-sec interrupt.
stz SCANINT ; reset 1-sec interrupt
jsr modem_shutdown
rep #$30
jsr cda_shutdown
lda MasterID
beq :e
pha
pha
_DisposeAll
_DeleteID
:e
pea #0
pld
sec
xce
mx %00
inc $3f4 ; invalidate power-up bit
jsr $bf00
db $65
dw :parms
brk $ea
:parms db 4
db 0
dw 0
db 0
dw 0
*dispatch ent
* mx %11
** a = character to xmit
* bit LOCAL
* bmi :local
* jmp write_modem
*:local
* pha
* jsr disable_cursor
* pla
* fall through
vt100
mx %11
and #$7f
cmp #' '
bcs :notctrl
asl
tax
jmp (:ctrl_table,x)
:notctrl
ldx state
jmp (:state_table,x)
:state_table
ext vt52_esc,vt52_dca
ext vt100_esc,vt100_csi,vt100_csi_2
ext vt100_esc_pound,vt100_esc_lparen,vt100_esc_rparen
ext vt100_esc_bad,vt100_csi_bad
ext draw_char,draw_char_raw
dw draw_char
dw vt52_esc
dw vt52_dca
dw draw_char
dw vt100_esc
dw vt100_csi
dw vt100_csi_2
dw vt100_esc_pound
dw vt100_esc_lparen
dw vt100_esc_rparen
dw vt100_esc_bad
dw vt100_csi_bad
:ctrl_table
dw ctrl_00,ctrl_01,ctrl_02,ctrl_03
dw ctrl_04,ctrl_05,ctrl_06,ctrl_07
dw ctrl_08,ctrl_09,ctrl_0a,ctrl_0b
dw ctrl_0c,ctrl_0d,ctrl_0e,ctrl_0f
dw ctrl_10,ctrl_11,ctrl_12,ctrl_13
dw ctrl_14,ctrl_15,ctrl_16,ctrl_17
dw ctrl_18,ctrl_19,ctrl_1a,ctrl_1b
dw ctrl_1c,ctrl_1d,ctrl_1e,ctrl_1f
ctrl_00
ctrl_01
ctrl_02
ctrl_03
ctrl_04
ctrl_05 ; answer ENQ
ctrl_06
ctrl_0e ; G1 character set
ctrl_0f ; G0 character set
ctrl_10
ctrl_11 ; XON
ctrl_12
ctrl_13 ; XOFF
ctrl_14
ctrl_15
ctrl_16
ctrl_17
ctrl_19
ctrl_1c
ctrl_1d
ctrl_1e
ctrl_1f
rts
ctrl_07 ; ring the bell.
ext beep
jmp beep
ctrl_1b ; escape -
* vt100 - aborts current escape sequence and starts a new one.
* vt52 - esc esc aborts and starts new
* vt50 - esc esc aborts
bit DECANM
bpl :vt52
lda #st_vt100_esc
sta state
rts
:vt52
lda #st_vt52_esc
sta state
rts
ctrl_18
ctrl_1a
* vt100 - abort current escape sequence
* and display error character.
*
* based on testing, this applies to vt52 and vt100;
* cancel character is drawn regardless of current state.
lda x
and #$1
ora #$56 ; $56 or $57
* lda #$57
jsr draw_char_raw
bit DECANM
bpl :vt52
lda #st_vt100
sta state
rts
:vt52
lda #st_vt52
sta state
rts
ctrl_08 ; back space
lda x
beq :rts
and #$7f
dec
sta x
jmp recalc_cursor_x
:rts rts
ctrl_09 ; tab
* vt100 has adjustable tabs.
ext next_tab_stop
ldx x
bmi :rts
jsr next_tab_stop
stx x
jmp recalc_cursor_x
:rts rts
ctrl_0a ; line feed - cursor down w/ scroll
ctrl_0b ; vertical tab
ctrl_0c ; form feed.
* if LNM is active, equivalent to CR, LF
bit #LNM
bpl :lf
stz x
jsr recalc_cursor_x
:lf
lda y
cmp DECBM
bne :simple
* lda #" " ; needs to factor in reverse video
* sta cursor_saved_char
jmp scroll_down
* if LNM mode, need to update cursor as well.
:simple
cmp #23
beq :rts
inc y
jmp recalc_cursor_y
:rts rts
ctrl_0d ; carriage return - cursor to column 0.
stz x
jmp recalc_cursor_x
sav vt100.main.L

419
vt100.modem.S Normal file
View File

@ -0,0 +1,419 @@
lst off
rel
xc
xc
mx %11
cas se
use vt.equ
use debug
SCCBREG equ $c038
SCCAREG equ $c039
SCCBDATA equ $c03a
SCCADATA equ $c03b
SerFlag equ $e10104 ;
*
* scc speed:
*
* time constant = ( clock / (2 * clock mode * baud rate)) - 2
* baud rate = clock / ( 2 * clock mode * (time constant + 2))
*
* clock mode = 1x, 16x, 32x, or 64x (selected via write register 4 bits 6/7)
* clock = 3.6864 MHz crystal (scc runs at 14.31818 / 4 = ~ 3.58 Mhz)
* time constant = write register 12 (low) + 13 (high)
*
*
* see IIgs TN #18 - Do-It-Yourself SCC Access
modem_startup ent
enable_modem ent
* sep #$30
php
sei
stz read_q_head
stz read_q_tail
stz write_q_head
stz write_q_tail
* zero out the buffer [for CDA debugger]
ldx #0
]loop stz read_buffer,x
inx
bne ]loop
lda SCCBREG ; sync access
ldx #0
]loop
lda :table,x
bmi :done
sta SCCBREG
inx
lda :table,x
sta SCCBREG
inx
bra ]loop
:done
* adjust SerFlag so serial IRQs will be handled.
lda >SerFlag
ora #%00_000_111 ; channel B interrupts.
sta >SerFlag
plp
rts
:table ; register, value
* db 9,%01_0_1_0_0_0_1 ; reset channel B (modem port) - handled @ startup.
db 4,%01_00_01_0_0 ; x16 clock, 1 stop bit, no parity
db 3,%11_0_0_0_0_0_0 ; 8 bits, rx disabled
db 5,%0_11_0_0_0_1_0 ; 8 bits, RTS
db 11,%0_10_10_0_00 ; modem port, rcv/tx clock = br
db 12,10 ; 9600 baud (low)
db 13,0 ; 9600 baud (high)
db 14,0 ; disable baud rate generator
db 14,%000_0_0_0_0_1 ; enable baud rate generator
db 3,%11_0_0_0_0_0_1 ; 8 bits, rx enabled
db 5,%0_11_0_1_0_1_0 ; 8 bits, tx enabled, RTS
db 15,0 ; disable external interrupts
db 0,%00_010_0_00 ; reset ext/status interrupts
db 1,%0_0_0_10_0_0_0 ; interrupts on rx or special condition
db 9,%00_0_0_1_0_1_0 ; master interrupts enabled.
db -1,-1
disable_modem ent
* local mode
mx %11
php
sei
lda SCCBREG ; sync access
lda #9
sta SCCBREG
lda #%01_0_1_0_0_0_1 ; reset channel B.
sta SCCBREG
stz read_q_head
stz read_q_tail
stz write_q_head
stz write_q_tail
plp
rts
modem_shutdown ent
mx %11
php
sei
lda SCCBREG ; sync access
lda #9
sta SCCBREG
lda #%01_0_1_0_0_0_1 ; reset channel B.
sta SCCBREG
lda >SerFlag
and #%11_111_000 ; channel B interrupts.
sta >SerFlag
plp
rts
write_modem_sync ent
mx %11
* a: byte to send
tay ; save
* ldx #0
php
:mask = %0010_0100 ; tx buffer empty, clear to send
:wait
cli ; guard scc register access.
sei
stz SCCBREG
lda SCCBREG
and #:mask
cmp #:mask
bne :wait
sty SCCBDATA
plp
rts
read_modem_sync ent
* c set if data read
* v set if overrun
mx %11
* ldx #0
rep #$41 ; clear C + V
stz SCCBREG
lda SCCBREG
and #%0001
beq :rts
* read reg 1 for overrun
lda #1
sta SCCBREG
lda SCCBREG
and #%0010_0000
beq :ok
* clear the overrun
lda #$30 ; reg0, error reset.
sta SCCBREG
stz SCCBREG
sep #$40 ; V
:ok
* lda #8
* sta SCCBREG
* lda SCCBREG
lda SCCBDATA
* debugging...
sec
:rts rts
write_buffer equ $1d00
read_buffer equ $1e00
modem_vector ent
jml modem_int
modem_int
*
* called in 8-bit native mode, interrupts disabled.
* d = unknown
* a/x/y don't need to be preserved.
* return carry clear if handled, carry set if not.
* doesn't access direct page.
* check/clear overrun?
*
* n.b. - vt100 would drop $00 and $7f characters here - I drop them later.
*
mx %11
phb
phk
plb
lda SCCBREG ; sync
stz SCCBREG
lda SCCBREG
and #%0000_0001 ; rx ready.
beq :nope
:read
lda SCCBDATA
ldx DPAGE+read_q_head
sta read_buffer,x
inc DPAGE+read_q_head
* more?
stz SCCBREG
lda SCCBREG
and #%0000_0001 ; rx ready.
bne :read
clc
bra :finish
:nope
sec
:finish
* reset errors.
lda #%00_110_000
stz SCCBREG
sta SCCBREG
* reset highest ius
lda #%00_111_000
stz SCCBREG
sta SCCBREG
plb
rtl
modem_io ent
debug modem_io
mx %11
php
sei
bit LOCAL
bmi :local
:write
* send any outbound data...
:mask = %0010_0100 ; tx buffer empty, clear to send
ldx write_q_tail
cpx write_q_head
beq :read
lda SCCBREG ; sync
stz SCCBREG
lda SCCBREG
and #:mask
cmp #:mask
bne :read
* ldx write_q_tail
lda write_buffer,x
sta SCCBDATA
inc write_q_tail
:read
ldx read_q_tail
cpx read_q_head
beq :nope
lda read_buffer,x
inc read_q_tail
* $00 and $7f dropped here.
and #$7f
beq :read
cmp #$7f
beq :read
plp
sec
rts
:nope
plp
clc
rts
:local
ldx write_q_tail
cpx write_q_head
beq :nope
lda write_buffer,x
inc write_q_tail
plp
sec
rts
write_modem ent
write_modem_async ent
mx %11
php
sei
* bit LOCAL
* bmi :local
ldx write_q_head
sta write_buffer,x
inc write_q_head
plp
rts
*:local
* ldx read_q_head
* sta read_buffer,x
* inc read_q_head
* plp
* rts
write_modem_str ent
; y = address of string (0-terminated)
; inc write_q_head vs inx
; because it wraps at $ff
mx %10
php
sei
* bit LOCAL
* bmi :local
:loop lda |$0000,y
beq :fini
ldx write_q_head
sta write_buffer,x
inc write_q_head
iny
bra :loop
*:local lda |$0000,y
* beq :fini
* ldx read_q_head
* sta read_buffer,x
* inc read_q_head
* iny
* bra :local
:fini
plp
rts
read_modem ent
read_modem_async ent
mx %11
php
sei
ldx read_q_tail
cpx read_q_head
beq :nope
lda read_buffer,x
inc read_q_tail
plp
sec
rts
:nope
plp
clc
rts
reset_modem_buffer ent
mx %11
php
sei
stz read_q_head
stz read_q_tail
stz write_q_head
stz write_q_tail
plp
rts
*buffer ds 256
sav vt100.modem.L

830
vt100.screen.S Normal file
View File

@ -0,0 +1,830 @@
lst off
rel
xc
xc
use vt.equ
use apple2gs.equ
mx %11
* x 0-79
* y 0-23
* DECMT 0-22
* DECMB 1-23
*
* cursor_base - pointer to current line
* cursor_offset - index into current line
* cursor_saved_char - saved character under the cursor
* cursor_state - $80 = disabled, $40 = on
update_sgr ent
stz draw_inverse
lda SGR
beq :rts
lda #$80
sta draw_inverse
:rts rts
text
dw $0400
dw $0480
dw $0500
dw $0580
dw $0600
dw $0680
dw $0700
dw $0780
dw $0428
dw $04a8
dw $0528
dw $05a8
dw $0628
dw $06a8
dw $0728
dw $07a8
dw $0450
dw $04d0
dw $0550
dw $05d0
dw $0650
dw $06d0
dw $0750
dw $07d0
disable_cursor ent
mx %11
php
sei
bit cursor_state
bmi :rts
bvc :80
lda cursor_saved_char
ldy cursor_offset
sta [cursor_base],y
:80 lda #$80
tsb cursor_state
:rts plp
rts
enable_cursor ent
mx %11
php
sei
bit cursor_state
bpl :rts
bvc :80
* option for inverted cursor?
ldy cursor_offset
lda [cursor_base],y
sta cursor_saved_char
lda cursor_char
sta [cursor_base],y
:80 lda #$80
trb cursor_state
:rts plp
rts
cursor_vector ent
jml cursor_int
NumInts equ $e01d67
cursor_int
* cursor interrupt - blink the cursor.
mx %11
phb
phd
phk
plb
* check if CDA active.
* $ff = inactive, $00 = active
lda >NumInts
bpl :rts
pea DPAGE
pld
ldy cursor_offset
lda cursor_state
bmi :rts
eor #$40
sta cursor_state
beq :off
:on
lda [cursor_base],y
sta cursor_saved_char
lda cursor_char
sta [cursor_base],y
bra :rts
:off
lda cursor_saved_char
sta [cursor_base],y
:rts stz SCANINT ; reset 1-sec interrupt
pld
plb
clc
rtl
recalc_cursor ent
* recalculate the cursor pointer after x/y changed
* assumes cursor is off so no saving/restoring the cursor char.
mx %11
php
rep #$30
lda y
asl
tay
lda text,y
sta cursor_base
lda x
and #$7f
lsr
sta cursor_offset
stz cursor_base+2
bcs :ok
inc cursor_base+2
:ok plp
rts
recalc_cursor_x ent
mx %11
php
lda x
and #$7f
lsr
sta cursor_offset
stz cursor_base+2
bcs :ok
inc cursor_base+2
:ok plp
rts
recalc_cursor_y ent
mx %11
php
rep #$30
lda y
asl
tay
lda text,y
sta cursor_base
plp
rts
advance_x ent
mx %11
* ldx x
* cpx #79
* bcs :rts
inc x
lda #1
eor cursor_base+2
sta cursor_base+2
beq :rts
inc cursor_offset
:rts rts
draw_char ent
; a = char
* alternate character set
* 00 - 1f = uppercase inverse letters ('@' - '_')
* 00 - 3f = special characters, inverse (' ' - '?')
* 40 - 4f = mouse text
* 60 - 7f = lower case letters, inverse
* 80 - 9f = upper case letters, normal
* a9 - ff = special, upper, lower chars, normal.
* for normal letters, ora $80
* for inverse letters, uppercase need to be remapped to 0-1f
* others don't change.
bit draw_inverse
bpl :normal
; invert it.
cmp #$60 ; `, first lowercase
bge draw_char_raw ; nothing to do for lowercase
cmp #$40 ; @, first uppercase
bcc draw_char_raw ; nothing to do for special
:uc and #%10111111 ; ~ $40
bra draw_char_raw
:normal ora #$80
draw_char_raw ent
* entry point for writing character to screen w/o processing it
* sta cursor_saved_char
* with DECAWM, x = 79, will CR LF (with scroll) before drawing character.
* at column 79, x increases but cursor does not. up/down does not change
* overflow. backspace / left arrow will go to 78.
* x = 80 indicates next char will wrap if DECAWM. however, treated as 79
* based on testing, will not advance to column 80 unless DECAWM is enabled.
ldx x
cpx #79
bcs :rm
ldy cursor_offset
sta [cursor_base],y
jmp advance_x
:rm
beq :79
bit DECAWM
bmi :wrap
:79
ldy cursor_offset
sta [cursor_base],y
lda DECAWM ; set bit 7 if DECAWM.
tsb x ; mark overflow
rts
:wrap
stz x
ldy y
cpy DECBM
beq :scroll
cpy #23
beq :23 ;
inc y
:23 pha ; save character
jsr recalc_cursor
pla
sta [cursor_base] ; offset 0
jmp advance_x
:scroll
pha ; save
jsr scroll_down
jsr recalc_cursor_x
pla
sta [cursor_base] ; offset 0
jmp advance_x
* erase screen commands are not affected by origin or scrolling region.
erase_screen ent
erase_screen_2 ent
* erase the entire screen.
mx %11
lda erase_char
* fall through
fill_screen ent
* fill the entire screen with the a register.
* text screen is out of order, so this doesn't use much code but
* it's not linear either.
* +64 bytes of screen hole data.
mx %11
sta >$000400
sta >$010400
php
rep #$30
ldx #$0400
ldy #$0401
lda #40*3-2
mvn $010000,$010000
ldx #$0400
ldy #$0480
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0500
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0580
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0600
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0680
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0700
lda #40*3-1
mvn $010000,$010000
ldx #$0400
ldy #$0780
lda #40*3-1
mvn $010000,$010000
*
ldx #$0400
ldy #$0401
lda #40*3-2
mvn $000000,$000000
ldx #$0400
ldy #$0480
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0500
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0580
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0600
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0680
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0700
lda #40*3-1
mvn $000000,$000000
ldx #$0400
ldy #$0780
lda #40*3-1
mvn $000000,$000000
* not needed since $0,$0 last
* phk
* plb
plp
rts
* scroll...
* scroll will always be one line at a time
scroll_up ent
* move DECTM .. DECBM-1 -> DECTM+1 .. DECBM insert blank line at DECTM.
mx %11
php
rep #$30
lda DECBM
sec
sbc DECTM
sta r0
lda DECBM
asl ;
tax
jmp (:dispatch,x)
:dispatch
dw :00,:01,:02,:03,:04
dw :05,:06,:07,:08,:09
dw :10,:11,:12,:13,:14
dw :15,:16,:17,:18,:19
dw :20,:21,:22,:23
* mvn 1,1 first so mvn 0,0 will restore b
:cp mac
lda #40-1
ldx #]1
ldy #]2
mvn $010000,$010000
lda #40-1
ldx #]1
ldy #]2
mvn $000000,$000000
dec r0
bne *+5
brl :done
<<<
* number refers to the source line.
:23 :cp $0750;$07d0
:22 :cp $06d0;$0750
:21 :cp $0650;$06d0
:20 :cp $05d0;$0650
:19 :cp $0550;$05d0
:18 :cp $04d0;$0550
:17 :cp $0450;$04d0
:16 :cp $07a8;$0450
:15 :cp $0728;$07a8
:14 :cp $06a8;$0728
:13 :cp $0628;$06a8
:12 :cp $05a8;$0628
:11 :cp $0528;$05a8
:10 :cp $04a8;$0528
:09 :cp $0428;$04a8
:08 :cp $0780;$0428
:07 :cp $0700;$0780
:06 :cp $0680;$0700
:05 :cp $0600;$0680
:04 :cp $0580;$0600
:03 :cp $0500;$0580
:02 :cp $0480;$0500
:01 :cp $0400;$0480
:00
:done
* now clear DECTM line
* lda DECTM
* asl
* tay
* ldx text,y
ldx cursor_base
lda erase_char
sta cursor_saved_char
ldy #19
:loop
sta >$000000,x
sta >$010000,x
inx
inx
dey
bpl :loop
plp
rts
scroll_down ent
* move DECTM+1 .. DECBM -> DECTM .. DECBM-1, insert blank line at DECBM.
mx %11
php
rep #$30
lda DECBM
sec
sbc DECTM
sta r0
lda DECTM
asl ;
tax
jmp (:dispatch,x)
:dispatch
dw :00,:01,:02,:03,:04
dw :05,:06,:07,:08,:09
dw :10,:11,:12,:13,:14
dw :15,:16,:17,:18,:19
dw :20,:21,:22,:23
:cp mac
lda #40-1
ldx #]1
ldy #]2
mvn $010000,$010000
lda #40-1
ldx #]1
ldy #]2
mvn $000000,$000000
dec r0
bne *+5
brl :done
<<<
* todo -- fix offsets
* number refers to the dest line.
:00 :cp $0480;$0400
:01 :cp $0500;$0480
:02 :cp $0580;$0500
:03 :cp $0600;$0580
:04 :cp $0680;$0600
:05 :cp $0700;$0680
:06 :cp $0780;$0700
:07 :cp $0428;$0780
:08 :cp $04a8;$0428
:09 :cp $0528;$04a8
:10 :cp $05a8;$0528
:11 :cp $0628;$05a8
:12 :cp $06a8;$0628
:13 :cp $0728;$06a8
:14 :cp $07a8;$0728
:15 :cp $0450;$07a8
:16 :cp $04d0;$0450
:17 :cp $0550;$04d0
:18 :cp $05d0;$0550
:19 :cp $0650;$05d0
:20 :cp $06d0;$0650
:21 :cp $0750;$06d0
:22 :cp $07d0;$0750
:23
:done
* now clear DECBM line
* lda DECBM
* asl
* tay
* ldx text,y
ldx cursor_base
lda erase_char
sta cursor_saved_char
ldy #19
:loop
sta >$000000,x
sta >$010000,x
inx
inx
dey
bpl :loop
plp
rts
* erase 0 - cursor to end of line
* erase 1 - start of line to cursor
* erase 2 - erase line
erase_line_2 ent
mx %11
php
rep #$30
* lda y
* asl
* ldx text,y
ldx cursor_base
lda erase_char
ldy #19
:loop
sta >$000000,x
sta >$010000,x
inx
inx
dey
bpl :loop
plp
rts
erase_line_0 ent
*
* erase cursor to end of line.
*
mx %11
lda x
beq erase_line_2
php
rep #$30
lda cursor_base
clc
adc cursor_offset
tax
sep #$20 ; short m
* odd byte
ldy cursor_offset
lda cursor_base+2
bne :even
lda erase_char
sta [cursor_base],y
inx
iny
cpy #40
beq :exit
:even
lda erase_char
:loop sta >$010000,x
sta >$000000,x
inx
iny
cpy #40
blt :loop
:exit
plp
rts
erase_line_1 ent
* erase start of line to cursor.
mx %11
lda x
cmp #79
bcs erase_line_2
php
rep #$30
lda cursor_base
clc
adc cursor_offset
tax
sep #$20 ; short m
ldy cursor_offset
lda cursor_base+2
beq :odd
lda erase_char
sta [cursor_base],y
dex
dey
bmi :exit
:odd
lda erase_char
:loop sta >$010000,x
sta >$000000,x
dex
dey
bpl :loop
:exit plp
rts
erase_screen_0 ent
* erase cursor to end of screen.
mx %11
jsr erase_line_0
php
rep #$30
lda y
inc
cmp #24
bcs :exit
asl
tay
lda erase_char
:loop
sty r0
ldx text,y
ldy #19
:loop0
sta >$000000,x
inx
inx
dey
bpl :loop0
ldy r0
ldx text,y
ldy #19
:loop1
sta >$010000,x
inx
inx
dey
bpl :loop1
ldy r0
iny
iny
cpy #24*2
bcc :loop
:exit
plp
rts
erase_screen_1 ent
* erase beginning of screen to cursor.
mx %11
jsr erase_line_1
php
rep #$30
lda y
dec
bmi :exit
asl
tay
lda erase_char
:loop
sty r0
ldx text,y
ldy #19
:loop0
sta >$000000,x
inx
inx
dey
bpl :loop0
ldy r0
ldx text,y
ldy #19
:loop1
sta >$010000,x
inx
inx
dey
bpl :loop1
ldy r0
dey
dey
bpl :loop
:exit
plp
rts
rts
sav vt100.screen.L

77
vt100.tabs.S Normal file
View File

@ -0,0 +1,77 @@
lst off
rel
xc
xc
use vt.equ
mx %11
init_tabs ent
ldx #80-1
:zloop stz tabs,x
dex
bpl :zloop
lda #$80
ldy #8
:loop ldx :table,y
sta tabs,x
dey
bpl :loop
rts
:table db 8*1,8*2,8*3,8*4,8*5,8*6,8*7,8*8,8*9
set_tab ent
* input x = x
* ldx x
cpx #80
bge :rts
lda #$80
sta tabs,x
:rts rts
reset_tab ent
* input x = x
* ldx x
cpx #80
bge :rts
stz tabs,x
:rts rts
reset_all_tabs ent
ldx #80-1
:loop stz tabs,x
dex
bpl :loop
rts
next_tab_stop ent
* input x = x
* ldx x
inx
cpx #79
bge :79
:loop bit tabs,x
bmi :rts
inx
cpx #80
bcc :loop
:79 ldx #79
:rts rts
tabs ds 80
sav vt100.tabs.L

253
vt100.vt52.S Normal file
View File

@ -0,0 +1,253 @@
lst off
rel
xc
xc
use vt.equ
mx %11
*
* vt52 emulation for the vt100
*
* ESC < exits
ext write_modem,draw_char
ext advance_x,recalc_cursor,recalc_cursor_x,recalc_cursor_y
ext scroll_up,scroll_down
vt52_esc ent
* ABCDFGHIJKYZ<>=
ldx #st_vt52
stx state
cmp #:MIN
blt :rts
cmp #:MAX+1
bge :rts
sec
sbc #:MIN
asl
tax
jmp (:table,x)
:rts
rts
:MIN equ 60
:MAX equ 90
:table
dw esc_lt ; <
dw esc_eq ; =
dw esc_gt ; >
dw :rts ; ?
dw :rts ; @
dw esc_A ; A
dw esc_B ; B
dw esc_C ; C
dw esc_D ; D
dw :rts ; E
dw esc_F ; F
dw esc_G ; G
dw esc_H ; H
dw esc_I ; I
dw esc_J ; J
dw esc_K ; K
dw :rts ; L
dw :rts ; M
dw :rts ; N
dw :rts ; O
dw :rts ; P
dw :rts ; Q
dw :rts ; R
dw :rts ; S
dw :rts ; T
dw :rts ; U
dw :rts ; V
dw :rts ; W
dw :rts ; X
dw esc_Y ; Y
dw esc_Z ; Z
esc_lt
* based on testing, this also resets graphics mode
* (which we don't support anyhow.)
lda #$80
sta DECANM
* stz MOUSETEXT
lda #st_vt100
sta state
rts
esc_eq ; enter alternate keypad mode
lda #$80
sta DECKPAM
rts
esc_gt ; exit alternate keypad mode
stz DECKPAM
rts
* cursor movement respects the scrolling region.
esc_A ; cursor up.
lda y
beq :rts
cmp DECTM
beq :rts
dec y
jmp recalc_cursor_y
:rts rts
esc_B ; cursor down
lda y
cmp #23
beq :rts
cmp DECBM
beq :rts
inc y
jmp recalc_cursor_y
:rts rts
esc_C ; cursor right
lda x
cmp #79
bcs :rts
inc x
jmp recalc_cursor_x
:rts rts
esc_D ; cursor left
lda x
beq :rts
and #$7f
dec
sta x
jmp recalc_cursor_x
:rts rts
esc_I ; cursor up w/ line scroll
* based on testing, scrolling only occurs within the
* scroll region.
lda y
cmp DECTM
beq :scroll
cmp #0
beq :rts
dec y
jmp recalc_cursor_y
:scroll jmp scroll_up
:rts rts
esc_J
* erase cursor to end of screen.
ext erase_screen_0
jmp erase_screen_0
rts
esc_K
* erase cursor to end of line
ext erase_line_0
jmp erase_line_0
esc_H ; cursor home
; based on testing, does not respect scrolling region but does
; respect origin mode.
stz x
lda DECTM
sta y
:go jmp recalc_cursor
esc_F ; enter graphics mode
* lda #%0010
* tsb mode
lda #$80
* tsb MOUSETEXT
rts
esc_G ; exit graphics mode
* lda #%0010
* trb mode
* stz MOUSETEXT
rts
esc_Y ; direct cursor address
* vt100 - does not take effect until the end.
* based on testing, there is internal state information,
* so esc Y a esc B esc Y b is equivalent to esc Y a b
*
* if width exceeded, clamps at right margin.
* if height exceeded, does not change.
lda #st_vt52_dca
sta state
rts
esc_Z ; terminal identity.
; return ESC / Z
; based on testing, no display in local mode
bit LOCAL
bmi :local
lda #ESC
jsr write_modem
lda #'/'
jsr write_modem
lda #'Z'
jmp write_modem
:local rts
* lda #'Z'
* jmp draw_char
vt52_dca ent
* this differs from esc [ H in that invalid
* values are ignored rather than clamped.
* based on testing, does not respect DECOM.
* based on testing, state is saved if ESC aborts, even
* if switching to vt100 mode and back or ^X to cancel.
sec
sbc #' '
bit :tmp
bmi :go
ora #$80
sta :tmp
rts
:go ; a = x
cmp #80
bge :y
sta x
:y lda :tmp
and #$7f
cmp #24
bge :update
sta y
:update
stz :tmp
lda #st_vt52
sta state
jmp recalc_cursor
:tmp ds 2
sav vt100.vt52.L

131
vt52.S
View File

@ -21,6 +21,7 @@ START equ *
*
*
ESC equ $1b
SET80VID equ $c00d
@ -159,7 +160,7 @@ keypress
:remap
lda :arrowtable+1,x
sta key
lda #$1b
lda #ESC
jsr dispatch
bra :ctrl
@ -187,41 +188,104 @@ keypress
rts
keypad
* 0-9, Enter, . PF1-3 are the only valid keypad keys
lda mode
bit #mAltKeyPad
beq :normal
lda key
cmp #'0'
bcc :notnum
cmp #'9'+1
bcs :notnum
:qq
* 0-9 sends ESC ? p-y which is num | 0x40
lda #$1b
jsr dispatch
lda #'?'
jsr dispatch
lda key
ora #$40
jmp dispatch
:notnum
cmp #$0d
beq :qq
cmp #'.'
beq :qq
cmp #:MIN
bcc :rts
cmp #:MAX+1
bcc :ok
:rts rts
:ok
tay ; save
sec
sbc #:MIN
tax
lda :table,x
beq :rts ; dead
bmi :normal
:pf
bit mode ; don't bother in local mode
bmi :rts
pha ; save
lda #ESC
jsr write_modem
pla ; restore
jmp write_modem
:normal
* todo - PF1/2/3 should send ESC P/Q/R
lda key
; y still has key value
lda mode
and #mAltKeyPad
bne :alt
tya
jmp dispatch
:alt
phy ; save
bit mode
bmi :local
lda #ESC
jsr write_modem
lda #'?'
jsr write_modem
:local
pla
ora #$40
jmp dispatch
:table db $1b,'P'
db '=','Q'
db '/','R'
:MIN equ 13
:MAX equ 61
; 0x80 = ESC ? c + $40 in alt mode
:table
db $80 ; ^M Enter -> \r, ESC ? M
db $0 ; ^N
db $0 ; ^O
db $0 ; ^P
db $0 ; ^Q
db $0 ; ^R
db $0 ; ^S
db $0 ; ^T
db $0 ; ^U
db $0 ; ^V
db $0 ; ^W
db $0 ; ^X
db $0 ; ^Y
db $0 ; ^Z
db 'P' ; ^[ PF1 -> ESC P
db $0 ; ^\
db $0 ; ^]
db $0 ; ^^
db $0 ; ^_
db $0 ;
db $0 ; !
db $0 ; "
db $0 ; #
db $0 ; $
db $0 ; %
db $0 ; &
db $0 ; '
db $0 ; (
db $0 ; )
db $0 ; *
db $0 ; +
db $0 ; ,
db $0 ; -
db $80 ; .
db 'R' ; / PF3 -> ESC R
db $80 ; 0
db $80 ; 1
db $80 ; 2
db $80 ; 3
db $80 ; 4
db $80 ; 5
db $80 ; 6
db $80 ; 7
db $80 ; 8
db $80 ; 9
db $0 ; :
db $0 ; ;
db $0 ; <
db 'Q' ; = PF2 -> ESC Q
@ -480,7 +544,6 @@ clear_eol
sta [text01],y
sta (text00),y
iny
cpy #40
bra :even
:rts plp
rts
@ -842,7 +905,7 @@ ctrl_1b ; escape -
ctrl_09 ; tab
lda x
cmp #73
cmp #72
bcs :one
clc
@ -958,7 +1021,7 @@ esc_Z ; identify terminal.
; return ESC / K
bit mode
bmi :local
lda #$1b
lda #ESC
jsr write_modem
lda #'/'
jsr write_modem