Merge branch 'next-release'
This commit is contained in:
commit
780fb5ee01
11 changed files with 6067 additions and 5866 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
layout: page
|
||||
title: DEC VT100 Terminal
|
||||
title: DEC VT100 Terminal with Debugger
|
||||
permalink: /devices/pc8080/machine/vt100/debugger/
|
||||
machines:
|
||||
- type: pc8080
|
||||
|
|
@ -13,10 +13,22 @@ DEC VT100 Terminal with Debugger
|
|||
|
||||
The [PC8080](/modules/pc8080/) machine below is configured to simulate a [VT100 Terminal](/devices/pc8080/machine/vt100/)
|
||||
with a Control Panel and Debugger. It is running the original [VT100 Firmware](/devices/pc8080/rom/vt100/) inside the
|
||||
[PC8080](/modules/pc8080/) CPU emulator.
|
||||
[PC8080](/modules/pc8080/) CPU emulator. You'll also find assorted [Hardware Notes](#vt100-memory-usage) below.
|
||||
|
||||
Click the "Run" button to start the simulation. You'll also find assorted
|
||||
[Hardware Notes](#vt100-memory-usage) below.
|
||||
Click the "Run" button to start the simulation. The VT100 KEYMAP table in [keyboard.js](/modules/pc8080/lib/keyboard.js)
|
||||
maps modern keys to VT100 key addresses, and most of the mappings are 1-1. Function keys are mapped as follows:
|
||||
|
||||
- F1: PF1
|
||||
- F2: PF2
|
||||
- F3: PF3
|
||||
- F4: PF4
|
||||
- F5: KEYPAD COMMA
|
||||
- F6: BREAK
|
||||
- F7: LINE FEED
|
||||
- F8: NO SCROLL
|
||||
- F9: SET-UP
|
||||
|
||||
The simulation is not fully operational yet, but it is now possible to access the SET-UP screen.
|
||||
|
||||
{% include machine.html id="vt100" %}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<machine id="vt100" class="pc8080" border="1" pos="center" background="#FAEBD7">
|
||||
<name pos="center">VT100 Terminal</name>
|
||||
<computer id="computer" busWidth="16"/>
|
||||
<cpu id="cpu8080" model="8080" cycles="2764800"/>
|
||||
<cpu id="cpu8080" model="8080" cycles="1000000"/>
|
||||
<rom id="rom" addr="0x0000" size="0x2000" file="/devices/pc8080/rom/vt100/VT100.json"/>
|
||||
<ram id="ram" addr="0x2000" size="0x0C00"/>
|
||||
<video id="video" screenWidth="1600" screenHeight="960" smoothing="false" interruptRate="60"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<machine id="vt100" class="pc8080" border="1" pos="center" background="#FAEBD7">
|
||||
<name pos="center">VT100 Terminal</name>
|
||||
<computer id="computer" busWidth="16"/>
|
||||
<cpu id="cpu8080" model="8080" cycles="2764800"/>
|
||||
<cpu id="cpu8080" model="8080" cycles="1000000"/>
|
||||
<rom id="rom" addr="0x0000" size="0x2000" file="/devices/pc8080/rom/vt100/VT100.json"/>
|
||||
<ram id="ram" addr="0x2000" size="0x0C00"/>
|
||||
<video id="video" screenWidth="1600" screenHeight="960" smoothing="false" interruptRate="60"
|
||||
|
|
|
|||
|
|
@ -6,60 +6,75 @@
|
|||
; work at https://github.com/phooky/VT100-Hax/blob/master/ROMs/haxrom.d80 and http://vt100romhax.tumblr.com
|
||||
;
|
||||
; All other comments/annotations by Jeff Parsons (@jeffpar) <Jeff@pcjs.org>
|
||||
;
|
||||
|
||||
;
|
||||
; Data area definitions
|
||||
;
|
||||
stack_top equ 204eh
|
||||
scratch_start equ 204eh
|
||||
cursor_countdn equ 212dh
|
||||
cursor_visible equ 21bah
|
||||
|
||||
;
|
||||
; Reset vector
|
||||
;
|
||||
org 0
|
||||
|
||||
X0000: di
|
||||
lxi sp,X204e
|
||||
jmp X003b
|
||||
|
||||
di
|
||||
lxi sp,stack_top ; SP = 0x204e
|
||||
jmp reset
|
||||
;
|
||||
; Interrupt vector 0x1 for Keyboard
|
||||
;
|
||||
org 8
|
||||
;
|
||||
; Interrupt vector 0x1 for keyboard
|
||||
;
|
||||
X0008: call X00fd
|
||||
call X00fd
|
||||
ei
|
||||
ret
|
||||
|
||||
X000d: nop
|
||||
|
||||
;
|
||||
; Interrupt vector 0x2 for PUSART
|
||||
;
|
||||
org 10h
|
||||
|
||||
call X03cc
|
||||
ei
|
||||
X0014: ret
|
||||
|
||||
nop
|
||||
|
||||
ret
|
||||
;
|
||||
; Interrupt vector 0x3 for Keyboard and PUSART
|
||||
;
|
||||
org 18h
|
||||
|
||||
call X03cc
|
||||
call X00fd
|
||||
ei
|
||||
ret
|
||||
|
||||
;
|
||||
; Interrupt vector 0x4 for Video (vertical retrace)
|
||||
;
|
||||
org 20h
|
||||
call X04cf
|
||||
ret
|
||||
|
||||
nop
|
||||
|
||||
;
|
||||
; Interrupt vector 0x5 for Keyboard and Video
|
||||
;
|
||||
org 28h
|
||||
|
||||
call X04cf
|
||||
ret
|
||||
|
||||
nop
|
||||
|
||||
;
|
||||
; Interrupt vector 0x6 for PUSART and Video
|
||||
;
|
||||
org 30h
|
||||
|
||||
X0030: call X03cc
|
||||
call X04cf
|
||||
ei
|
||||
ret
|
||||
|
||||
;
|
||||
; Interrupt vector 0x7 for Keyboard and PUSART and Video
|
||||
;
|
||||
org 38h
|
||||
jmp X0030
|
||||
|
||||
X003b: mvi e,1
|
||||
;
|
||||
; Beginning of power-up (reset) code at 0x003b
|
||||
;
|
||||
reset: mvi e,1
|
||||
X003d: di
|
||||
mvi a,0fh
|
||||
out 62h ;; 0x0f to NVR latch
|
||||
|
|
@ -233,7 +248,7 @@ X00fd: push psw
|
|||
sui 7ch ;; subtract 0x7c from a
|
||||
jm X011a ;; jump to 011a if a is a normal key
|
||||
mov h,a ;; otherwise h <- a
|
||||
X0109: inr h ;; h++
|
||||
inr h ;; h++
|
||||
mvi a,10h ;; a <- 0x10
|
||||
rrc
|
||||
X010d: rlc
|
||||
|
|
@ -370,7 +385,7 @@ X01ff: lxi h,X0812
|
|||
X020e: sui 41h
|
||||
mov b,a
|
||||
X0211: mov a,m
|
||||
X0212: jz X0222
|
||||
jz X0222
|
||||
dcr b
|
||||
jz X0226
|
||||
dcr b
|
||||
|
|
@ -408,7 +423,7 @@ X0245: mov a,c
|
|||
jmp X0812
|
||||
|
||||
X0251: di
|
||||
lxi sp,X204e
|
||||
lxi sp,stack_top ; SP = 0x204e
|
||||
lxi h,2000h
|
||||
push h
|
||||
mov a,m
|
||||
|
|
@ -447,10 +462,10 @@ X0298: call X0bf2
|
|||
;;
|
||||
;; Zero all scratch and screen RAM above stack
|
||||
;;
|
||||
X02a4: lxi h,X204e ;; HL <- 0x204e (top of stack)
|
||||
lxi d,X0fb2 ;; DE <- 0x0fb2
|
||||
mvi b,0 ;; B <- 0
|
||||
call X1083 ;; memset(0x204e,0,0xfb2)
|
||||
X02a4: lxi h,scratch_start ;; HL = 0x204e (just above stack)
|
||||
lxi d,0fb2h ;; DE = 0x0fb2 (every byte from 0x204e up to 0x3000)
|
||||
mvi b,0 ;; B = 0
|
||||
call memset ;; memset(0x1083): set 0x0fb2 bytes to 0 starting at 0x204e
|
||||
cma ;; invert A
|
||||
sta X2104 ;; store A in 0x2104
|
||||
lxi h,X2004 ;; HL <- 0x2004
|
||||
|
|
@ -469,7 +484,7 @@ X02c0: call X0a15 ;; 0x2140 = 0xe605
|
|||
lxi h,X3000 ;; HL = 0x3000
|
||||
lxi d,X1000 ;; DE = 0x1000
|
||||
mvi b,0ffh ;; B = 0xFF
|
||||
jmp X1083 ;; Set all of attribute RAM to 0xFF and return
|
||||
jmp memset ;; invoke memset(0x1083) to set all of attribute RAM to 0xFF and return
|
||||
;;
|
||||
;; 0x02d9 through 0x02eb are the initial values for the screen RAM;
|
||||
;; it's copied into low RAM at 0x2000 during init.
|
||||
|
|
@ -484,8 +499,8 @@ X02d9:
|
|||
;;
|
||||
;; Initialize several scratch values. We don't know what all of these are for yet.
|
||||
;;
|
||||
X02eb: lxi h,X0212 ;; HL = 0x0212
|
||||
shld X212d ;; Store 0x1202 at 0x212d
|
||||
X02eb: lxi h,212h ;; HL = 0x0212 (530.)
|
||||
shld cursor_countdn ;; Store 0x0212 at cursor_countdn [0x212d]
|
||||
mvi a,35h ;; A = 0x35
|
||||
sta X212c ;; 0x212c = 0x35
|
||||
mvi a,1 ;; A = 1
|
||||
|
|
@ -504,7 +519,7 @@ X02eb: lxi h,X0212 ;; HL = 0x0212
|
|||
sta X2079 ;; else store 1 in 0x2079
|
||||
X031a: mvi a,0ffh ;; A = 0xff
|
||||
sta X210e ;; 0x210e = 0xff
|
||||
sta X21ba ;; 0x21ba = 0xff
|
||||
sta cursor_visible ;; set cursor_visible [0x21ba] to 0xff (initially visible)
|
||||
mvi h,80h ;; H = 0x80
|
||||
mov l,h ;; L = 0x80
|
||||
shld X20c0 ;; Store 0x8080 at 0x20c0
|
||||
|
|
@ -1592,7 +1607,7 @@ X0a7b: inx sp
|
|||
|
||||
mvi a,48h
|
||||
sta X207e
|
||||
lxi h,X0000
|
||||
lxi h,0
|
||||
shld X2130
|
||||
pop h
|
||||
X0aa2: lxi h,X0a15
|
||||
|
|
@ -2405,7 +2420,7 @@ X0fae: cmp m
|
|||
rz
|
||||
push h
|
||||
push psw
|
||||
X0fb2: lda X207b
|
||||
lda X207b
|
||||
ora a
|
||||
cz X1488
|
||||
pop psw
|
||||
|
|
@ -2415,11 +2430,11 @@ X0fb2: lda X207b
|
|||
; End of main loop?
|
||||
;
|
||||
|
||||
X0fbe: lhld X204e
|
||||
X0fbe: lhld scratch_start ; HL = [0x204e]
|
||||
mov a,h
|
||||
ori 0f0h
|
||||
mov h,a
|
||||
shld X204e
|
||||
shld scratch_start ; update [0x204e]
|
||||
X0fc8: lda X2050
|
||||
X0fcb: mov b,a
|
||||
mov a,h
|
||||
|
|
@ -2525,15 +2540,14 @@ X1074: call X13e3
|
|||
shld X214e
|
||||
ret
|
||||
;;
|
||||
;; memset: Set memory from HL to HL+DE-1 to value B
|
||||
;; memset(0x1083): Set DE bytes at address HL to B
|
||||
;;
|
||||
memset:
|
||||
X1083: mov m,b ;; M <- B
|
||||
memset: mov m,b ;; M <- B
|
||||
inx h ;; HL++
|
||||
dcx d ;; DE--
|
||||
mov a,d ;;
|
||||
ora e ;; A = D | E
|
||||
jnz X1083 ;; repeat if A != 0
|
||||
jnz memset ;; repeat if A != 0
|
||||
ret
|
||||
|
||||
mvi c,0
|
||||
|
|
@ -2672,7 +2686,7 @@ X1159: mov m,e
|
|||
X117e: mov a,h
|
||||
ori 0f0h
|
||||
mov h,a
|
||||
shld X204e
|
||||
shld scratch_start ; update [0x204e]
|
||||
lxi h,X2113
|
||||
xra a
|
||||
X1189: mov m,a
|
||||
|
|
@ -2727,7 +2741,7 @@ X11ce: ora a
|
|||
mov l,m
|
||||
ori 0f0h
|
||||
mov h,a
|
||||
shld X204e
|
||||
shld scratch_start ; update [0x204e]
|
||||
call X1290
|
||||
pop h
|
||||
mov b,d
|
||||
|
|
@ -2747,7 +2761,7 @@ X11e8: call X127f
|
|||
ori 0f0h
|
||||
mov h,a
|
||||
mov l,e
|
||||
shld X204e
|
||||
shld scratch_start ; update [0x204e]
|
||||
pop h
|
||||
mov a,h
|
||||
ori 9fh
|
||||
|
|
@ -2810,12 +2824,12 @@ X1252: call X129d
|
|||
mov c,m
|
||||
call X13de
|
||||
xchg
|
||||
lhld X204e
|
||||
lhld scratch_start ; load [0x204e]
|
||||
xchg
|
||||
mov m,e
|
||||
dcx h
|
||||
mov m,d
|
||||
X1271: lhld X204e
|
||||
X1271: lhld scratch_start ; load [0x204e]
|
||||
shld X2056
|
||||
call X1299
|
||||
mov m,b
|
||||
|
|
@ -3175,25 +3189,29 @@ X14a2: lxi h,X21a5 ;;
|
|||
lda X2077 ;; load what's at 0x2077
|
||||
ora a ;; return if it's not zero
|
||||
rnz ;;
|
||||
lda X2065 ;; A = 0x2065
|
||||
lda X2065 ;; A = [0x2065]
|
||||
lxi h,X2051 ;;
|
||||
ora m ;; return if [0x2065] | [0x2051] != 0
|
||||
rnz
|
||||
lhld X212d
|
||||
|
||||
lhld cursor_countdn ; HL = cursor countdown from [0x212d]
|
||||
dcx h
|
||||
mov a,h
|
||||
ora l
|
||||
jz X14d4
|
||||
shld X212d
|
||||
shld cursor_countdn ; update cursor countdown with decremented value
|
||||
ret
|
||||
|
||||
X14d4: lda X21ba
|
||||
xri 0ffh
|
||||
sta X21ba
|
||||
lxi h,X0212
|
||||
jnz X14e5
|
||||
lxi h,X0109
|
||||
X14e5: shld X212d
|
||||
;
|
||||
; Cursor countdown reached zero, time to invert the cursor;
|
||||
; note that the cursor is on (visible) twice as long as it's off
|
||||
;
|
||||
X14d4: lda cursor_visible ; A = 0xff (visible) or 0x00 (invisible)
|
||||
xri 0ffh ; invert
|
||||
sta cursor_visible ;
|
||||
lxi h,212h ; new initial count; start with 0x0212
|
||||
jnz X14e5 ; and stick with that if cursor still visible
|
||||
lxi h,109h ; otherwise, use half that value (0x0109) if invisible
|
||||
X14e5: shld cursor_countdn ; store new cursor countdown
|
||||
lhld X20f6
|
||||
mov b,m
|
||||
lda X2159
|
||||
|
|
@ -3394,10 +3412,10 @@ X162b: inx h
|
|||
inx h
|
||||
jmp X13db
|
||||
|
||||
X1636: lxi h,X000d
|
||||
shld X212d
|
||||
X1636: lxi h,13
|
||||
shld cursor_countdn ; reset cursor countdown to 13 (0x000d)
|
||||
xra a
|
||||
sta X21ba
|
||||
sta cursor_visible ; reset cursor visibility
|
||||
lhld X20f6
|
||||
lda X20f4
|
||||
mov b,a
|
||||
|
|
@ -3679,7 +3697,7 @@ X1837: cmp d
|
|||
X1844: mov m,a
|
||||
jmp X1636
|
||||
|
||||
X1848: lxi h,X0000
|
||||
X1848: lxi h,0
|
||||
shld X2130
|
||||
mvi a,0ffh
|
||||
sta X210e
|
||||
|
|
@ -3786,7 +3804,7 @@ X1900: in 42h
|
|||
out 62h
|
||||
lxi d,X21d3 ;; DE = 0x21d3
|
||||
mvi b,0eh ;; B = 14
|
||||
lxi h,X0000 ;; HL = 0
|
||||
lxi h,0 ;; HL = 0
|
||||
X1916: dad h ;;
|
||||
ldax d ;; load next char
|
||||
ani 20h ;; and with NVR data bit
|
||||
|
|
@ -3967,7 +3985,7 @@ X1a2b: mvi c,80h
|
|||
shld X2111
|
||||
lxi h,X19ee
|
||||
shld X2140
|
||||
lxi h,X0000
|
||||
lxi h,0
|
||||
shld X2143
|
||||
shld X2172
|
||||
X1a45: lhld X2004
|
||||
|
|
@ -4170,7 +4188,7 @@ X1bb0: call X0394
|
|||
shld X2140
|
||||
ret
|
||||
|
||||
X1bba: lhld X204e
|
||||
X1bba: lhld scratch_start ; load [0x204e]
|
||||
jmp X1bc3
|
||||
|
||||
X1bc0: lxi h,X21cc
|
||||
|
|
@ -4374,7 +4392,7 @@ X1d17: mvi m,0
|
|||
lda X21ac
|
||||
call X1d61
|
||||
call X1c9f
|
||||
lxi d,X0008
|
||||
lxi d,8
|
||||
mvi c,4
|
||||
pop psw
|
||||
jz X1d3d
|
||||
|
|
@ -4600,8 +4618,8 @@ X1e9f: call X1edd
|
|||
inx h
|
||||
shld X21b4
|
||||
mov b,a
|
||||
lxi d,X0014
|
||||
call X1083
|
||||
lxi d,20 ; DE = 20(0x0014)
|
||||
call memset ; call memset(0x1083)
|
||||
lxi h,X1eb8
|
||||
shld X2140
|
||||
ret
|
||||
|
|
@ -4635,7 +4653,7 @@ X1ee5: call X05e3
|
|||
ret
|
||||
|
||||
X1eea: push h
|
||||
lxi h,X204e
|
||||
lxi h,scratch_start ; HL -> 0x204e
|
||||
mov c,a
|
||||
lda X21a2
|
||||
ora a
|
||||
|
|
@ -4804,7 +4822,6 @@ X1ccd equ 1ccdh
|
|||
X2001 equ 2001h
|
||||
X2002 equ 2002h
|
||||
X2004 equ 2004h
|
||||
X204e equ 204eh
|
||||
X2050 equ 2050h
|
||||
X2051 equ 2051h
|
||||
X2052 equ 2052h
|
||||
|
|
@ -4818,7 +4835,7 @@ X2065 equ 2065h
|
|||
X2067 equ 2067h
|
||||
X2068 equ 2068h ; Keys flag buffer
|
||||
X2069 equ 2069h
|
||||
X206a equ 206ah
|
||||
X206a equ 206ah ; Start of the "three place New Key Address Buffer"
|
||||
X206e equ 206eh
|
||||
X2072 equ 2072h
|
||||
X2073 equ 2073h
|
||||
|
|
@ -4857,7 +4874,6 @@ X2111 equ 2111h
|
|||
X2113 equ 2113h
|
||||
X212b equ 212bh
|
||||
X212c equ 212ch
|
||||
X212d equ 212dh
|
||||
X212f equ 212fh
|
||||
X2130 equ 2130h
|
||||
X2131 equ 2131h
|
||||
|
|
@ -4912,7 +4928,6 @@ X21af equ 21afh
|
|||
X21b4 equ 21b4h
|
||||
X21b8 equ 21b8h
|
||||
X21b9 equ 21b9h
|
||||
X21ba equ 21bah
|
||||
X21bb equ 21bbh
|
||||
X21bc equ 21bch
|
||||
X21bd equ 21bdh
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -169,13 +169,29 @@ ChipSet.SI1978 = {
|
|||
|
||||
/*
|
||||
* One of the many chips in the VT100 is an 8224, which operates at 24.8832MHz. That frequency is divided by 9
|
||||
* to yield a 361.69ns clock period for the 8080 CPU, which means the CPU is running at 2.76Mhz (cycles per second).
|
||||
* Hence the CPU component in the VT100's machine.xml is defined as:
|
||||
* to yield a 361.69ns clock period for the 8080 CPU, which means (in theory) that the CPU is running at 2.76Mhz.
|
||||
*
|
||||
* Hence the CPU component in the VT100's machine.xml SHOULD be defined as:
|
||||
*
|
||||
* <cpu id="cpu8080" model="8080" cycles="2764800"/>
|
||||
*
|
||||
* where 2764800 = 24883200 / 9. You need to know this because we rely on the CPU frequency for simulating some
|
||||
* of the other VT100 circuits.
|
||||
* where 2764800 = 24883200 / 9. Unfortunately, the VT100 ROM decrements a countdown value in memory to determine
|
||||
* cursor blink rate, and if we use 2764800 cycles per second, the cursor blinks MUCH too fast. It's surprising that
|
||||
* the VT100 doesn't rely on vertical retrace interrupts for blink rate. Perhaps the designers were concerned about
|
||||
* consistency across 60Hz and 50Hz display modes, although that seems like a minor concern, considering that the
|
||||
* alternative means the ROM is now tied to a specific CPU operating frequency. However, short of rewriting portions
|
||||
* of the ROM, we have to deal with it.
|
||||
*
|
||||
* And we deal with it by lowering cycles per second to 1000000 (1Mhz). I'm guessing that in a real VT100, the 8080
|
||||
* gets bogged down by other factors (eg, the Video Processor's DMA requests), but we don't simulate the hardware to
|
||||
* that level of detail, so the easiest solution is to lower the effective clock speed.
|
||||
*
|
||||
* NOTE: If you've noticed that the VT100 cursor blinks unevenly, you're right, and it's by design: the ROM uses a
|
||||
* countdown value for the cursor's "on" state that is twice as large as that for the cursor's "off" state, so it's
|
||||
* "on" twice as long as it's "off".
|
||||
*
|
||||
* WARNING: The choice of clock speed has an effect on other simulated VT100 circuits; see the DC011 Timing Chip
|
||||
* discussion below, along with the getVT100LBA() function.
|
||||
*
|
||||
* For reference, here is a list of all the VT100 I/O ports, from /devices/pc8080/machine/vt100/debugger/README.md,
|
||||
* which in turn comes from p. 4-17 of the VT100 Technical Manual (July 1982):
|
||||
|
|
@ -386,6 +402,7 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.cmp = cmp;
|
||||
this.kbd = cmp.getMachineComponent("Keyboard");
|
||||
bus.addPortInputTable(this, this.config.portsInput);
|
||||
bus.addPortOutputTable(this, this.config.portsOutput);
|
||||
};
|
||||
|
|
@ -728,7 +745,7 @@ ChipSet.prototype.outSIWatchdog = function(port, b, addrFrom)
|
|||
};
|
||||
|
||||
/**
|
||||
* getVT100LBA(nBit)
|
||||
* getVT100LBA(iBit)
|
||||
*
|
||||
* Returns the state of the requested (simulated) LBA bit.
|
||||
*
|
||||
|
|
@ -737,12 +754,12 @@ ChipSet.prototype.outSIWatchdog = function(port, b, addrFrom)
|
|||
* period than if we divided the cycle count by 88, but a shorter LBA7 period is probably helpful in terms of
|
||||
* overall performance.
|
||||
*
|
||||
* @param {number} nBit
|
||||
* @param {number} iBit
|
||||
* @return {number}
|
||||
*/
|
||||
ChipSet.prototype.getVT100LBA = function(nBit)
|
||||
ChipSet.prototype.getVT100LBA = function(iBit)
|
||||
{
|
||||
return (this.cpu.getCycles() & (1 << (nBit - 1))) << 1;
|
||||
return (this.cpu.getCycles() & (1 << (iBit - 1))) << 1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -852,6 +869,10 @@ ChipSet.prototype.inVT100FlagsBuffer = function(port, addrFrom)
|
|||
if (this.bNVROut) {
|
||||
b |= ChipSet.VT100.FLAGS_BUFFER.NVR_DATA;
|
||||
}
|
||||
b &= ~ChipSet.VT100.FLAGS_BUFFER.KBD_XMIT;
|
||||
if (this.kbd && !this.kbd.checkBusy()) {
|
||||
b |= ChipSet.VT100.FLAGS_BUFFER.KBD_XMIT;
|
||||
}
|
||||
this.bFlagsBuffer = b;
|
||||
this.printMessageIO(port, null, addrFrom, "FLAGS.BUFFER", b);
|
||||
return b;
|
||||
|
|
@ -888,6 +909,10 @@ ChipSet.prototype.outVT100NVRLatch = function(port, b, addrFrom)
|
|||
/**
|
||||
* outVT100DC012(port, b, addrFrom)
|
||||
*
|
||||
* TODO: Consider whether we should disable any interrupts (eg, vertical retrace) until the
|
||||
* this port is initialized at runtime. We initialize it ourselves at start-up, but our initial
|
||||
* value is just a guess.
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0xA2)
|
||||
* @param {number} b
|
||||
|
|
|
|||
|
|
@ -939,6 +939,7 @@ CPUState.prototype.checkINTR = function()
|
|||
{
|
||||
if ((this.intFlags & CPUDef.INTFLAG.INTR) && this.getIF()) {
|
||||
var bRST = CPUDef.OPCODE.RST0 | ((this.intFlags & CPUDef.INTFLAG.INTL) << 3);
|
||||
this.intFlags &= ~CPUDef.INTFLAG.HALT;
|
||||
this.clearINTR();
|
||||
this.clearIF();
|
||||
this.aOps[bRST].call(this);
|
||||
|
|
@ -1096,16 +1097,30 @@ CPUState.prototype.stepCPU = function(nMinCycles)
|
|||
|
||||
do {
|
||||
if (this.intFlags) {
|
||||
if (this.checkINTR()) {
|
||||
if (!nMinCycles) {
|
||||
this.assert(DEBUGGER); // nMinCycles of zero should be generated ONLY by the Debugger
|
||||
if (DEBUGGER) {
|
||||
this.println("interrupt dispatched");
|
||||
break;
|
||||
/*
|
||||
* We no longer call checkINTR() if the Debugger is single-stepping; you'll have to let the
|
||||
* CPU run with a "g" (or a "p" on a call instruction) if you want interrupts to be processed.
|
||||
*/
|
||||
if (nMinCycles) {
|
||||
/*
|
||||
* NOTE: If checkINTR() returns true, it also clears INTFLAG.HALT, so we don't have to worry
|
||||
* about the INTFLAG.HALT code below triggering.
|
||||
*/
|
||||
this.checkINTR();
|
||||
/*
|
||||
* If the Debugger is running, consider some new notification mechanism(s) regarding interrupt
|
||||
* dispatches; the following code no longer applies, due to changes above.
|
||||
*
|
||||
* if (!nMinCycles && this.checkINTR()) {
|
||||
* this.assert(DEBUGGER); // nMinCycles of zero should be generated ONLY by the Debugger
|
||||
* if (DEBUGGER) {
|
||||
* this.println("interrupt dispatched");
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.intFlags & CPUDef.INTFLAG.HALT) {
|
||||
if (this.intFlags & CPUDef.INTFLAG.HALT) {
|
||||
/*
|
||||
* As discussed in opHLT(), the CPU is never REALLY halted by a HLT instruction; instead,
|
||||
* opHLT() sets CPUDef.INTFLAG.HALT, signalling to us that we're free to end the current burst
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ Keyboard.ASCII = {
|
|||
Keyboard.KEYCODE = {
|
||||
/* 0x08 */ BS: 8,
|
||||
/* 0x09 */ TAB: 9,
|
||||
/* 0x0A */ LF: 10,
|
||||
/* 0x0A */ LF: 10, // TODO: Determine if any key actually generates this (I suspect there is none)
|
||||
/* 0x0D */ CR: 13,
|
||||
/* 0x10 */ SHIFT: 16,
|
||||
/* 0x11 */ CTRL: 17,
|
||||
|
|
@ -249,7 +249,7 @@ Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.FF_DASH] = Keyboard.ASCII['-'];
|
|||
Keyboard.MINPRESSTIME = 100; // 100ms
|
||||
|
||||
/**
|
||||
* Alternate keyCode mappings (to support the popular WASD directional mappings)
|
||||
* Alternate keyCode mappings (to support popular "WASD"-style directional-key mappings)
|
||||
*
|
||||
* TODO: ES6 computed property name support may now be in all mainstream browsers, allowing us to use
|
||||
* a simple object literal for this and all other object initializations.
|
||||
|
|
@ -286,10 +286,11 @@ Keyboard.VT100 = {
|
|||
*
|
||||
* Every time a keyboard scan is initiated (by setting the START bit of the status byte),
|
||||
* an internal address index is reset to zero, and an interrupt is generated for each entry
|
||||
* in the aKeysPressed array, along with a final interrupt for KEYLAST.
|
||||
* in the aKeysActive array, along with a final interrupt for KEYLAST.
|
||||
*/
|
||||
ADDRESS: {
|
||||
PORT: 0x82
|
||||
PORT: 0x82,
|
||||
INIT: 0x7F
|
||||
},
|
||||
/*
|
||||
* Writing port 0x82 updates the VT100's keyboard status byte via the keyboard's UART data input.
|
||||
|
|
@ -329,39 +330,56 @@ Keyboard.VT100.KEYMAP[Keyboard.ASCII.W] = 0x09;
|
|||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.Q] = 0x0A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.RIGHT] = 0x10;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[']']] = 0x14;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['}']] = 0x94;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['[']] = 0x15;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['{']] = 0x95;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.I] = 0x16;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.U] = 0x17;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.R] = 0x18;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.E] = 0x19;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['1']] = 0x1A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['!']] = 0x9A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.LEFT] = 0x20;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.DOWN] = 0x22;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F6] = 0x23; // aka BREAK
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.PAUSE] = 0x23; // aka BREAK
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['`']] = 0x24;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['~']] = 0xA4;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['-']] = 0x25;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['_']] = 0xA5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['9']] = 0x26;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['(']] = 0xA6;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['7']] = 0x27;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['&']] = 0xA7;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['4']] = 0x28;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['$']] = 0xA8;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['3']] = 0x29;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['#']] = 0xA9;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.ESC] = 0x2A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.UP] = 0x30;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F3] = 0x31; // aka PF3
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F1] = 0x32; // aka PF1
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.BS] = 0x33;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['=']] = 0x34;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['+']] = 0xB4;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['0']] = 0x35;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[')']] = 0xB5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['8']] = 0x36;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['*']] = 0xB6;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['6']] = 0x37;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['^']] = 0xB7;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['5']] = 0x38;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['%']] = 0xB8;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['2']] = 0x39;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['@']] = 0xB9;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.TAB] = 0x3A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_7] = 0x40;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F4] = 0x41; // aka PF4
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F2] = 0x42; // aka PF2
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_0] = 0x43;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.LF] = 0x44;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F7] = 0x44; // aka LINE FEED
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['\\']] = 0x45;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['|']] = 0xC5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.L] = 0x46;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.K] = 0x47;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.G] = 0x48;
|
||||
|
|
@ -372,33 +390,38 @@ Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_CR] = 0x51;
|
|||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_2] = 0x52;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_1] = 0x53;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII["'"]] = 0x55;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['"']] = 0xD5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[';']] = 0x56;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[':']] = 0xD6;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.J] = 0x57;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.H] = 0x58;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.D] = 0x59;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.S] = 0x5A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_DEL] = 0x60; // keypad period
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F8] = 0x61; // aka keypad comma
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F5] = 0x61; // aka KEYPAD COMMA
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_5] = 0x62;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_4] = 0x63;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.CR] = 0x64; // TODO: Figure out why the Technical Manual lists CR at both 0x04 and 0x64
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['.']] = 0x65;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['>']] = 0xE5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[',']] = 0x66;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['<']] = 0xE6;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.N] = 0x67;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.B] = 0x68;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.X] = 0x69;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F9] = 0x6A; // aka NO SCROLL
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F8] = 0x6A; // aka NO SCROLL
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_9] = 0x70;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_3] = 0x71;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_6] = 0x72;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_SUB] = 0x73;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.NUM_SUB] = 0x73; // aka KEYPAD MINUS
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['/']] = 0x75;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII['?']] = 0xF5;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.M] = 0x76;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII[' ']] = 0x77;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.V] = 0x78;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.C] = 0x79;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.ASCII.Z] = 0x7A;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F10] = 0x7B; // aka SET-UP
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.F9] = 0x7B; // aka SET-UP
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.CTRL] = 0x7C;
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.SHIFT] = 0x7D; // either shift key (doesn't matter)
|
||||
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.CAPSLOCK]= 0x7E;
|
||||
|
|
@ -560,8 +583,9 @@ Keyboard.prototype.powerDown = function(fSave, fShutdown)
|
|||
|
||||
Keyboard.VT100.INIT = [
|
||||
[
|
||||
Keyboard.VT100.STATUS.INIT,
|
||||
0 // iKeyNext
|
||||
Keyboard.VT100.STATUS.INIT, // bVT100Status
|
||||
Keyboard.VT100.ADDRESS.INIT, // bVT100Address
|
||||
-1 // iKeyNext
|
||||
]
|
||||
];
|
||||
|
||||
|
|
@ -574,7 +598,7 @@ Keyboard.prototype.reset = function()
|
|||
{
|
||||
/*
|
||||
* As keyDown events are encountered, a corresponding "softCode" is looked up. If one is found,
|
||||
* then an entry for the key is added to the aKeysPressed array. Each entry contains:
|
||||
* then an entry for the key is added to the aKeysActive array. Each "key" entry in aKeysActive contains:
|
||||
*
|
||||
* softCode: number or string representing the key pressed
|
||||
* msDown: timestamp of the most recent "down" event
|
||||
|
|
@ -582,7 +606,7 @@ Keyboard.prototype.reset = function()
|
|||
*
|
||||
* When the key is finally released (or auto-released), its entry is removed from the array.
|
||||
*/
|
||||
this.aKeysPressed = [];
|
||||
this.aKeysActive = [];
|
||||
|
||||
if (this.config.INIT && !this.restore(this.config.INIT)) {
|
||||
this.notice("reset error");
|
||||
|
|
@ -604,7 +628,7 @@ Keyboard.prototype.save = function()
|
|||
case Keyboard.SI1978.MODEL:
|
||||
break;
|
||||
case Keyboard.VT100.MODEL:
|
||||
state.set(0, [this.bVT100Status]);
|
||||
state.set(0, [this.bVT100Status, this.bVT100Address, -1]);
|
||||
break;
|
||||
}
|
||||
return state.data();
|
||||
|
|
@ -630,7 +654,8 @@ Keyboard.prototype.restore = function(data)
|
|||
case Keyboard.VT100.MODEL:
|
||||
this.bVT100Status = a[0];
|
||||
this.updateLEDs(this.bVT100Status & Keyboard.VT100.STATUS.LEDS);
|
||||
this.iKeyNext = a[1];
|
||||
this.bVT100Address = a[1];
|
||||
this.iKeyNext = a[2];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -717,7 +742,7 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
|
|||
}
|
||||
|
||||
if (!COMPILED && this.messageEnabled(Messages.KEYS)) {
|
||||
this.printMessage("onKey" + (fDown? "Down" : "Up") + "(" + keyCode + "): " + (fPass? "true" : "false"), true);
|
||||
this.printMessage("onKey" + (fDown? "Down" : "Up") + "(" + keyCode + "): softCode=" + softCode + ", pass=" + (fPass? "true" : "false"), true);
|
||||
}
|
||||
|
||||
return fPass;
|
||||
|
|
@ -728,13 +753,13 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
|
|||
*
|
||||
* @this {Keyboard}
|
||||
* @param {number|string} softCode
|
||||
* @return {number} index of softCode in aKeysPressed, or -1 if not found
|
||||
* @return {number} index of softCode in aKeysActive, or -1 if not found
|
||||
*/
|
||||
Keyboard.prototype.indexOfSoftKey = function(softCode)
|
||||
{
|
||||
var i;
|
||||
for (i = 0; i < this.aKeysPressed.length; i++) {
|
||||
if (this.aKeysPressed[i].softCode == softCode) return i;
|
||||
for (i = 0; i < this.aKeysActive.length; i++) {
|
||||
if (this.aKeysActive[i].softCode == softCode) return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
|
@ -753,30 +778,30 @@ Keyboard.prototype.onSoftKeyDown = function(softCode, fDown)
|
|||
if (fDown) {
|
||||
// this.println(softCode + " down");
|
||||
if (i < 0) {
|
||||
this.aKeysPressed.push({
|
||||
this.aKeysActive.push({
|
||||
softCode: softCode,
|
||||
msDown: Date.now(),
|
||||
fAutoRelease: false
|
||||
});
|
||||
} else {
|
||||
this.aKeysPressed[i].msDown = Date.now();
|
||||
this.aKeysPressed[i].fAutoRelease = false;
|
||||
this.aKeysActive[i].msDown = Date.now();
|
||||
this.aKeysActive[i].fAutoRelease = false;
|
||||
}
|
||||
} else if (i >= 0) {
|
||||
// this.println(softCode + " up");
|
||||
if (!this.aKeysPressed[i].fAutoRelease) {
|
||||
var msDown = this.aKeysPressed[i].msDown;
|
||||
if (!this.aKeysActive[i].fAutoRelease) {
|
||||
var msDown = this.aKeysActive[i].msDown;
|
||||
if (msDown) {
|
||||
var msElapsed = Date.now() - msDown;
|
||||
if (msElapsed < Keyboard.MINPRESSTIME) {
|
||||
// this.println(softCode + " released after only " + msElapsed + "ms");
|
||||
this.aKeysPressed[i].fAutoRelease = true;
|
||||
this.aKeysActive[i].fAutoRelease = true;
|
||||
this.checkSoftKeysToRelease();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.aKeysPressed.splice(i, 1);
|
||||
this.aKeysActive.splice(i, 1);
|
||||
} else {
|
||||
// this.println(softCode + " up with no down?");
|
||||
}
|
||||
|
|
@ -820,10 +845,10 @@ Keyboard.prototype.checkSoftKeysToRelease = function()
|
|||
{
|
||||
var i = 0;
|
||||
var msDelayMin = -1;
|
||||
while (i < this.aKeysPressed.length) {
|
||||
if (this.aKeysPressed[i].fAutoRelease) {
|
||||
var softCode = this.aKeysPressed[i].softCode;
|
||||
var msDown = this.aKeysPressed[i].msDown;
|
||||
while (i < this.aKeysActive.length) {
|
||||
if (this.aKeysActive[i].fAutoRelease) {
|
||||
var softCode = this.aKeysActive[i].softCode;
|
||||
var msDown = this.aKeysActive[i].msDown;
|
||||
var msElapsed = Date.now() - msDown;
|
||||
var msDelay = Keyboard.MINPRESSTIME - msElapsed;
|
||||
if (msDelay > 0) {
|
||||
|
|
@ -849,9 +874,28 @@ Keyboard.prototype.checkSoftKeysToRelease = function()
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* checkBusy()
|
||||
*
|
||||
* Called whenever a ChipSet circuit needs the keyboard's UART status.
|
||||
* Currently, we have no busy conditions (our virtual keyboard is infinitely fast).
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @return {boolean}
|
||||
*/
|
||||
Keyboard.prototype.checkBusy = function()
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* inVT100UARTAddress(port, addrFrom)
|
||||
*
|
||||
* We take our cue from iKeyNext. If it's -1 (default), we simply return the last value latched
|
||||
* in bVT100Address. Otherwise, if iKeyNext is a valid index into aKeysActive, we look up the key
|
||||
* in the VT100.KEYMAP, latch it, and increment iKeyNext, else we latch Keyboard.VT100.KEYLAST
|
||||
* and set iKeyNext to -1 again.
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @param {number} port (0x82)
|
||||
* @param {number} [addrFrom] (not defined if the Debugger is trying to write the specified port)
|
||||
|
|
@ -859,12 +903,24 @@ Keyboard.prototype.checkSoftKeysToRelease = function()
|
|||
*/
|
||||
Keyboard.prototype.inVT100UARTAddress = function(port, addrFrom)
|
||||
{
|
||||
var b = 0;
|
||||
if (this.iKeyNext >= 0 && this.iKeyNext < this.aKeysPressed.length - 1) {
|
||||
var softCode = this.aKeysPressed[this.iKeyNext++];
|
||||
b = Keyboard.VT100.KEYMAP[softCode];
|
||||
var b = this.bVT100Address;
|
||||
if (this.iKeyNext >= 0) {
|
||||
if (this.iKeyNext < this.aKeysActive.length) {
|
||||
var key = this.aKeysActive[this.iKeyNext++];
|
||||
b = Keyboard.VT100.KEYMAP[key.softCode];
|
||||
if (b & 0x80) {
|
||||
/*
|
||||
* TODO: This code is supposed to be accompanied by a SHIFT key; make sure that it is.
|
||||
*/
|
||||
b &= 0x7F;
|
||||
}
|
||||
} else {
|
||||
this.iKeyNext = -1;
|
||||
b = Keyboard.VT100.KEYLAST;
|
||||
}
|
||||
this.bVT100Address = b;
|
||||
this.cpu.requestINTR(1);
|
||||
}
|
||||
if (!b) b = Keyboard.VT100.KEYLAST;
|
||||
this.printMessageIO(port, null, addrFrom, "KBDUART.ADDRESS", b);
|
||||
return b;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -320,7 +320,10 @@ Video.prototype.initBuffers = function()
|
|||
* for each font and draw characters by drawing from the font canvas to the target canvas.
|
||||
*/
|
||||
if (this.cxCell > 1) {
|
||||
this.initCellCache(this.nColsBuffer * this.nRowsBuffer);
|
||||
/*
|
||||
* We add an extra column per row to store the visible line length at the start of every row.
|
||||
*/
|
||||
this.initCellCache((this.nColsBuffer + 1) * this.nRowsBuffer);
|
||||
} else {
|
||||
this.imageBuffer = this.contextScreen.createImageData(cxBuffer, cyBuffer);
|
||||
this.nPixelsPerCell = (16 / this.nBitsPerPixel)|0;
|
||||
|
|
@ -959,11 +962,12 @@ Video.prototype.updateChar = function(idFont, col, row, data, context)
|
|||
};
|
||||
|
||||
/**
|
||||
* updateVT100()
|
||||
* updateVT100(fForced)
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video.prototype.updateVT100 = function()
|
||||
Video.prototype.updateVT100 = function(fForced)
|
||||
{
|
||||
var addrNext = this.addrBuffer, fontNext = -1;
|
||||
|
||||
|
|
@ -979,6 +983,8 @@ Video.prototype.updateVT100 = function()
|
|||
var nCols = 0;
|
||||
var addr = addrNext;
|
||||
var font = fontNext;
|
||||
var nColsVisible = this.nColsBuffer;
|
||||
if (font != Video.VT100.FONT.NORML) nColsVisible >>= 1;
|
||||
while (true) {
|
||||
var data = this.bus.getByteDirect(addr++);
|
||||
if ((data & Video.VT100.LINETERM) == Video.VT100.LINETERM) {
|
||||
|
|
@ -988,7 +994,7 @@ Video.prototype.updateVT100 = function()
|
|||
addrNext += (b & Video.VT100.LINEATTR.ADDRBIAS)? Video.VT100.ADDRBIAS_LO : Video.VT100.ADDRBIAS_HI;
|
||||
break;
|
||||
}
|
||||
if (nCols < this.abLineBuffer.length) {
|
||||
if (nCols < nColsVisible) {
|
||||
this.abLineBuffer[nCols++] = data;
|
||||
} else {
|
||||
break; // ideally, we would wait for a LINETERM byte, but it's not safe to loop without limit
|
||||
|
|
@ -1011,13 +1017,21 @@ Video.prototype.updateVT100 = function()
|
|||
}
|
||||
|
||||
/*
|
||||
* Display the line buffer; ordinarily, the font number would always be valid after processing the "fill lines",
|
||||
* but if the buffer isn't initialized yet, the usual LINETERM might be missing, so the font number might not be set.
|
||||
* Display the line buffer; ordinarily, the font number would be valid after processing the "fill lines",
|
||||
* but if the buffer isn't initialized yet, those lines might be missing, so the font number might not be set.
|
||||
*/
|
||||
if (font >= 0) {
|
||||
/*
|
||||
* Cell cache logic is complicated by the fact that a line may be single-width one frame and double-width
|
||||
* the next. So we store the visible line length at the start of each row in the cache, which must match if
|
||||
* the cache is considered valid for the current line.
|
||||
*/
|
||||
var fLineCacheValid = this.fCellCacheValid && (this.aCellCache[iCell] == nColsVisible);
|
||||
this.aCellCache[iCell++] = nColsVisible;
|
||||
for (var iCol = 0; iCol < nCols; iCol++) {
|
||||
data = this.abLineBuffer[iCol];
|
||||
if (!this.fCellCacheValid || data !== this.aCellCache[iCell]) {
|
||||
if (!fLineCacheValid || data !== this.aCellCache[iCell]) {
|
||||
this.aCellCache[iCell] = data;
|
||||
this.updateChar(font, iCol, nRows, data, this.contextBuffer);
|
||||
cUpdated++;
|
||||
}
|
||||
|
|
@ -1027,6 +1041,18 @@ Video.prototype.updateVT100 = function()
|
|||
nRows++;
|
||||
}
|
||||
|
||||
this.assert(font < 0 || iCell === this.nCellCache);
|
||||
|
||||
if (MAXDEBUG && !fForced) {
|
||||
var nSeconds = Date.now() / 1000;
|
||||
if ((nSeconds|0) != (this.nUpdateSeconds|0)) {
|
||||
this.nUpdateNumber = 0;
|
||||
}
|
||||
this.nUpdateNumber++;
|
||||
this.nUpdateSeconds = nSeconds;
|
||||
this.printMessage("updateVT100(): update #" + this.nUpdateNumber + " at " +this.nUpdateSeconds + " corner=" + str.toHexByte(this.aCellCache[1]) + " cycles=" + this.nCyclesPrev + " delta=" + this.nCyclesDelta);
|
||||
}
|
||||
|
||||
this.fCellCacheValid = true;
|
||||
|
||||
if (cUpdated && this.contextBuffer) {
|
||||
|
|
@ -1056,9 +1082,10 @@ Video.prototype.updateScreen = function(n)
|
|||
{
|
||||
var fClean;
|
||||
var fUpdate = true;
|
||||
var fForced = true;
|
||||
|
||||
if (n >= 0) {
|
||||
|
||||
fForced = false;
|
||||
if (this.rateInterrupt) {
|
||||
/*
|
||||
* TODO: Incorporate these hard-coded interrupt vector numbers into configuration blocks.
|
||||
|
|
@ -1093,11 +1120,11 @@ Video.prototype.updateScreen = function(n)
|
|||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
if (DEBUG && !fForced) {
|
||||
var nCycles = this.cpu.getCycles();
|
||||
var nCyclesDelta = nCycles - this.nCyclesPrev;
|
||||
this.nCyclesDelta = nCycles - this.nCyclesPrev;
|
||||
this.nCyclesPrev = nCycles;
|
||||
this.printMessage("updateScreen(" + n + "): clean=" + fClean + ", update=" + fUpdate + ", cycles=" + nCycles + ", delta=" + nCyclesDelta);
|
||||
if (MAXDEBUG) this.printMessage("updateScreen(" + n + "): clean=" + fClean + ", update=" + fUpdate + ", cycles=" + this.nCyclesPrev + ", delta=" + this.nCyclesDelta);
|
||||
}
|
||||
|
||||
if (!fUpdate) {
|
||||
|
|
@ -1105,32 +1132,34 @@ Video.prototype.updateScreen = function(n)
|
|||
}
|
||||
|
||||
if (this.cxCell > 1) {
|
||||
this.updateScreenText();
|
||||
this.updateScreenText(fForced);
|
||||
} else {
|
||||
this.updateScreenGraphics();
|
||||
this.updateScreenGraphics(fForced);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* updateScreenText()
|
||||
* updateScreenText(fForced)
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video.prototype.updateScreenText = function()
|
||||
Video.prototype.updateScreenText = function(fForced)
|
||||
{
|
||||
switch(this.nFormat) {
|
||||
case Video.FORMAT.VT100:
|
||||
this.updateVT100();
|
||||
this.updateVT100(fForced);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* updateScreenGraphics()
|
||||
* updateScreenGraphics(fForced)
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video.prototype.updateScreenGraphics = function()
|
||||
Video.prototype.updateScreenGraphics = function(fForced)
|
||||
{
|
||||
var addr = this.addrBuffer;
|
||||
var addrLimit = addr + this.sizeBuffer;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue