From a0d89e50717df6208b35b560620ee597123bdebe Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Sat, 27 Jun 2015 10:31:26 -0700 Subject: [PATCH] Generic write mode 0 function was missing set/reset capability --- modules/pcjs/lib/cpu.js | 6 +- modules/pcjs/lib/video.js | 37 +++++----- modules/pcjs/lib/x86cpu.js | 2 +- tests/pc/vga/L23-1.ASM | 138 ++++++++++++++++++------------------- tests/pc/vga/L24-1.ASM | 45 ++++++------ tests/pc/vga/L25-3.ASM | 1 + tests/pc/vga/README.md | 4 +- 7 files changed, 121 insertions(+), 112 deletions(-) diff --git a/modules/pcjs/lib/cpu.js b/modules/pcjs/lib/cpu.js index ea8b2941f..f03a3d9d3 100644 --- a/modules/pcjs/lib/cpu.js +++ b/modules/pcjs/lib/cpu.js @@ -690,7 +690,7 @@ CPU.prototype.calcCycles = function(fRecalc) */ CPU.prototype.getCycles = function(fScaled) { - var nCycles = this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles; + var nCycles = (this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles)|0; if (fScaled && this.aCounts.nCyclesMultiplier > 1 && this.aCounts.mhz > this.aCounts.mhzDefault) { /* * We could scale the current cycle count by the current effective speed (this.aCounts.mhz); eg: @@ -787,10 +787,11 @@ CPU.prototype.getSpeedTarget = function() /** * setSpeed(nMultiplier, fOnClick) * + * NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point. + * * @this {CPU} * @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high) * @param {boolean} [fOnClick] is true if called from a click handler that might have stolen focus - * @return {number} the target speed, in mhz * @desc Whenever the speed is changed, the running cycle count and corresponding start time must be reset, * so that the next effective speed calculation obtains sensible results. In fact, when runCPU() initially calls * setSpeed() with no parameters, that's all this function does (it doesn't change the current speed setting). @@ -819,7 +820,6 @@ CPU.prototype.setSpeed = function(nMultiplier, fOnClick) this.aCounts.msStartRun = usr.getTime(); this.aCounts.msEndThisRun = 0; this.calcCycles(); - return this.aCounts.mhzTarget; }; /** diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index feb27ac07..ea23469c1 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -1677,6 +1677,19 @@ Card.ACCESS.readByteMode1 = function readByteMode1(off, addr) /** * writeByteMode0(off, b, addr) * + * Supporting Set/Reset means that for every plane for which Set/Reset is enabled, we must + * replace the corresponding byte in "dw" with a byte of zeros or ones. This is accomplished with + * nSetMapMask, nSetMapData, and nSetMapBits. nSetMapMask is the inverse of the ESRESET bits, + * because we use it to mask the processor data; nSetMapData records the desired SRESET bits; and + * nSetMapBits contains the bits to replace those that we masked in the processor data. + * + * We could have done this: + * + * dw = (dw & this.controller.nSetMapMask) | (this.controller.nSetMapData & ~this.controller.nSetMapMask) + * + * but by maintaining nSetMapBits equal to (nSetMapData & ~nSetMapMask), we are able to make the writes + * slightly more efficient. + * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) @@ -1686,7 +1699,8 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr) { var idw = off + this.offset; var dw = b | (b << 8) | (b << 16) | (b << 24); - dw = (this.adw[idw] & ~this.controller.nWriteMapMask) | (dw & this.controller.nWriteMapMask); + dw = (dw & this.controller.nSetMapMask) | this.controller.nSetMapBits; + dw = (dw & this.controller.nWriteMapMask) | (this.adw[idw] & ~this.controller.nWriteMapMask); dw = (dw & this.controller.nBitMapMask) | (this.controller.latches & ~this.controller.nBitMapMask); if (this.adw[idw] != dw) { this.adw[idw] = dw; @@ -1723,19 +1737,6 @@ Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b, addr) /** * writeByteMode0Rot(off, b, addr) * - * Supporting Set/Reset means that for every plane for which Set/Reset is enabled, we must - * replace the corresponding byte in "dw" with a byte of zeros or ones. This is accomplished with - * nSetMapMask, nSetMapData, and nSetMapBits. nSetMapMask is the inverse of the ESRESET bits, - * because we use it to mask the processor data; nSetMapData records the desired SRESET bits; and - * nSetMapBits contains the bits to replace those that we masked in the processor data. - * - * We could have done this: - * - * dw = (dw & this.controller.nSetMapMask) | (this.controller.nSetMapData & ~this.controller.nSetMapMask) - * - * but by maintaining nSetMapBits equal to (nSetMapData & ~nSetMapMask), we are able to make the writes - * slightly more efficient. - * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) @@ -5062,7 +5063,11 @@ Video.prototype.getRetraceBits = function(card) */ var nCycles = this.cpu.getCycles(); var nElapsedCycles = nCycles - card.nInitCycles; - if (nElapsedCycles < 0) nElapsedCycles = 0; // TODO: Determine if this ever happens + if (nElapsedCycles < 0) { + this.assert(nCycles === 0); + card.nInitCycles = nElapsedCycles; + nElapsedCycles = -nElapsedCycles|0; + } var nCyclesHorzRemain = nElapsedCycles % card.nCyclesHorzPeriod; if (nCyclesHorzRemain > card.nCyclesHorzActive) b |= Card.CGA.STATUS.DISP_RETRACE; var nCyclesVertRemain = nElapsedCycles % card.nCyclesVertPeriod; @@ -5076,7 +5081,7 @@ Video.prototype.getRetraceBits = function(card) * * NOTE: Now that we're calling getRetraceBits() more frequently (ie, for internal checks), resetting nInitCycles * in this fashion preserves the vertical period at the expense of the horizontal period, which in turn can cause - * grief in ROM BIOS code that requires strict horizontal retrace times. So the above code is now disabled. + * grief in ROM BIOS code that requires strict horizontal retrace times. TODO: Figure out how to re-enable this code. */ return b; }; diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 9f2afb69f..b9dc6eee3 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -166,7 +166,7 @@ function X86CPU(parmsCPU) * A variety of stepCPU() state variables that don't strictly need to be initialized before the first * stepCPU() call, but it's good form to do so. */ - this.nBurstCycles = 0; + this.resetCycles(); this.aFlags.fComplete = this.aFlags.fDebugCheck = false; /* diff --git a/tests/pc/vga/L23-1.ASM b/tests/pc/vga/L23-1.ASM index ff8a72f6d..0824acf6c 100644 --- a/tests/pc/vga/L23-1.ASM +++ b/tests/pc/vga/L23-1.ASM @@ -15,39 +15,39 @@ VIDEO_SEGMENT equ 0a000h ;display memory segment for LOGICAL_SCREEN_WIDTH equ 672/8 ;width in bytes and height in scan LOGICAL_SCREEN_HEIGHT equ 384 ; lines of the virtual screen ; we'll work with -PAGE0 equ 0 ;flag for page 0 when page flipping -PAGE1 equ 1 ;flag for page 1 when page flipping -PAGE0_OFFSET equ 0 ;start offset of page 0 in VGA memory +PAGE0 equ 0 ;flag for page 0 when page flipping +PAGE1 equ 1 ;flag for page 1 when page flipping +PAGE0_OFFSET equ 0 ;start offset of page 0 in VGA memory PAGE1_OFFSET equ LOGICAL_SCREEN_WIDTH * LOGICAL_SCREEN_HEIGHT - ;start offset of page 1 (both pages - ; are 672x384 virtual screens) -BALL_WIDTH equ 24/8 ;width of ball in display memory bytes -BALL_HEIGHT equ 24 ;height of ball in scan lines -BLANK_OFFSET equ PAGE1_OFFSET * 2 ;start of blank image - ; in VGA memory + ;start offset of page 1 (both pages + ; are 672x384 virtual screens) +BALL_WIDTH equ 24/8 ;width of ball in display memory bytes +BALL_HEIGHT equ 24 ;height of ball in scan lines +BLANK_OFFSET equ PAGE1_OFFSET * 2;start of blank image + ; in VGA memory BALL_OFFSET equ BLANK_OFFSET + (BALL_WIDTH * BALL_HEIGHT) - ;start offset of ball image in VGA memory -NUM_BALLS equ 4 ;number of balls to animate + ;start offset of ball image in VGA memory +NUM_BALLS equ 4 ;number of balls to animate ; ; VGA register equates. ; -SC_INDEX equ 3c4h ;SC index register -MAP_MASK equ 2 ;SC map mask register -GC_INDEX equ 3ceh ;GC index register -GC_MODE equ 5 ;GC mode register -CRTC_INDEX equ 03d4h ;CRTC index register -START_ADDRESS_HIGH equ 0ch ;CRTC start address high byte -START_ADDRESS_LOW equ 0dh ;CRTC start address low byte -CRTC_OFFSET equ 13h ;CRTC offset register -INPUT_STATUS_1 equ 03dah ;VGA status register -VSYNC_MASK equ 08h ;vertical sync bit in status register 1 -DE_MASK equ 01h ;display enable bit in status register 1 -AC_INDEX equ 03c0h ;AC index register -HPELPAN equ 20h OR 13h ;AC horizontal pel panning register - ; (bit 7 is high to keep palette RAM - ; addressing on) +SC_INDEX equ 3c4h ;SC index register +MAP_MASK equ 2 ;SC map mask register +GC_INDEX equ 3ceh ;GC index register +GC_MODE equ 5 ;GC mode register +CRTC_INDEX equ 03d4h ;CRTC index register +START_ADDRESS_HIGH equ 0ch ;CRTC start address high byte +START_ADDRESS_LOW equ 0dh ;CRTC start address low byte +CRTC_OFFSET equ 13h ;CRTC offset register +INPUT_STATUS_1 equ 03dah ;VGA status register +VSYNC_MASK equ 08h ;vertical sync bit in status register 1 +DE_MASK equ 01h ;display enable bit in status register 1 +AC_INDEX equ 03c0h ;AC index register +HPELPAN equ 20h OR 13h ;AC horizontal pel panning register + ; (bit 7 is high to keep palette RAM + ; addressing on) dseg segment para common 'DATA' -CurrentPage db PAGE1 ;page to draw to +CurrentPage db PAGE1 ;page to draw to CurrentPageOffset dw PAGE1_OFFSET ; ; Four plane's worth of multicolored ball image. @@ -128,15 +128,15 @@ PanningControlString dw 32, 1, 0, 34, 0, 1, 32, -1, 0, 34, 0, -1, 0 else PanningControlString dw 32, 1, 0, 184, 0, 1, 32, -1, 0, 184, 0, -1, 0 endif -PanningControl dw PanningControlString ;pointer to current location - ; in panning control string -PanningRep dw 1 ;# times to pan according to current - ; panning increments -PanningXInc dw 1 ;x panning factor -PanningYInc dw 0 ;y panning factor -HPan db 0 ;horizontal pel panning setting -PanningStartOffset dw 0 ;start offset adjustment to produce vertical - ; panning & coarse horizontal panning +PanningControl dw PanningControlString ;pointer to current location + ; in panning control string +PanningRep dw 1 ;# times to pan according to current + ; panning increments +PanningXInc dw 1 ;x panning factor +PanningYInc dw 0 ;y panning factor +HPan db 0 ;horizontal pel panning setting +PanningStartOffset dw 0 ;start offset adjustment to produce vertical + ; panning & coarse horizontal panning dseg ends ; ; Macro to set indexed register P2 of chip with index register @@ -172,31 +172,31 @@ endif ; Draw border around playfield in both pages. ; mov di,PAGE0_OFFSET - call DrawBorder ;page 0 border + call DrawBorder ;page 0 border mov di,PAGE1_OFFSET - call DrawBorder ;page 1 border + call DrawBorder ;page 1 border ; ; Draw all four plane's worth of the ball to undisplayed VGA memory. ; - mov al,01h ;enable plane 0 + mov al,01h ;enable plane 0 SETREG SC_INDEX, MAP_MASK mov si,offset BallPlane0Image mov di,BALL_OFFSET mov cx,BALL_WIDTH * BALL_HEIGHT rep movsb - mov al,02h ;enable plane 1 + mov al,02h ;enable plane 1 SETREG SC_INDEX, MAP_MASK mov si,offset BallPlane1Image mov di,BALL_OFFSET mov cx,BALL_WIDTH * BALL_HEIGHT rep movsb - mov al,04h ;enable plane 2 + mov al,04h ;enable plane 2 SETREG SC_INDEX, MAP_MASK mov si,offset BallPlane2Image mov di,BALL_OFFSET mov cx,BALL_WIDTH * BALL_HEIGHT rep movsb - mov al,08h ;enable plane 3 + mov al,08h ;enable plane 3 SETREG SC_INDEX, MAP_MASK mov si,offset BallPlane3Image mov di,BALL_OFFSET @@ -205,8 +205,8 @@ endif ; ; Draw a blank image the size of the ball to undisplayed VGA memory. ; - mov al,0fh ;enable all memory planes, since the - SETREG SC_INDEX, MAP_MASK ; blank has to erase all planes + mov al,0fh ;enable all memory planes, since the + SETREG SC_INDEX, MAP_MASK ; blank has to erase all planes mov di,BLANK_OFFSET mov cx,BALL_WIDTH * BALL_HEIGHT sub al,al @@ -216,13 +216,13 @@ endif ; mov dx,GC_INDEX mov al,GC_MODE - out dx,al ;point GC Index to GC Mode register - inc dx ;point to GC Data register - jmp $+2 ;delay to let bus settle - in al,dx ;get current state of GC Mode - and al,not 3 ;clear the write mode bits - or al,1 ;set the write mode field to 1 - jmp $+2 ;delay to let bus settle + out dx,al ;point GC Index to GC Mode register + inc dx ;point to GC Data register + jmp $+2 ;delay to let bus settle + in al,dx ;get current state of GC Mode + and al,not 3 ;clear the write mode bits + or al,1 ;set the write mode field to 1 + jmp $+2 ;delay to let bus settle out dx,al ; ; Set VGA offset register in words to define logical screen width. @@ -253,12 +253,12 @@ EachBallLoop: ; ; Change the ball movement values if it's time to do so. ; - dec [BallRep+bx] ;has current repeat factor run out? + dec [BallRep+bx] ;has current repeat factor run out? jnz MoveBall - mov si,[BallControl+bx] ;it's time to change movement values - lodsw ;get new repeat factor from - ; control string - and ax,ax ;at end of control string? + mov si,[BallControl+bx] ;it's time to change movement values + lodsw ;get new repeat factor from + ; control string + and ax,ax ;at end of control string? jnz SetNewMove mov si,[BallControlString+bx] ;reset control string lodsw ;get new repeat factor @@ -366,18 +366,18 @@ start endp ; DrawBall proc near mov ax,LOGICAL_SCREEN_WIDTH - mul dx ;offset of start of top image scan line - add ax,cx ;offset of upper left of image + mul dx ;offset of start of top image scan line + add ax,cx ;offset of upper left of image add ax,[CurrentPageOffset] ;offset of start of page mov di,ax mov bp,BALL_HEIGHT push ds push es - pop ds ;move from VGA memory to VGA memory + pop ds ;move from VGA memory to VGA memory DrawBallLoop: push di mov cx,BALL_WIDTH - rep movsb ;draw a scan line of image + rep movsb ;draw a scan line of image pop di add di,LOGICAL_SCREEN_WIDTH ;point to next destination scan line dec bp @@ -418,7 +418,7 @@ WaitDisplayEnable endp ; Perform horizontal/vertical panning. ; AdjustPanning proc near - dec [PanningRep] ;time to get new panning values? + dec [PanningRep] ;time to get new panning values? jnz DoPan mov si,[PanningControl] ;point to current location in ; panning control string @@ -483,10 +483,10 @@ DrawBorder proc near push di mov cx,LOGICAL_SCREEN_HEIGHT / 16 DrawLeftBorderLoop: - mov al,0ch ;select red color for block + mov al,0ch ;select red color for block call DrawBorderBlock add di,LOGICAL_SCREEN_WIDTH * 8 - mov al,0eh ;select yellow color for block + mov al,0eh ;select yellow color for block call DrawBorderBlock add di,LOGICAL_SCREEN_WIDTH * 8 loop DrawLeftBorderLoop @@ -498,10 +498,10 @@ DrawLeftBorderLoop: add di,LOGICAL_SCREEN_WIDTH - 1 mov cx,LOGICAL_SCREEN_HEIGHT / 16 DrawRightBorderLoop: - mov al,0eh ;select yellow color for block + mov al,0eh ;select yellow color for block call DrawBorderBlock add di,LOGICAL_SCREEN_WIDTH * 8 - mov al,0ch ;select red color for block + mov al,0ch ;select red color for block call DrawBorderBlock add di,LOGICAL_SCREEN_WIDTH * 8 loop DrawRightBorderLoop @@ -513,10 +513,10 @@ DrawRightBorderLoop: mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2 DrawTopBorderLoop: inc di - mov al,0eh ;select yellow color for block + mov al,0eh ;select yellow color for block call DrawBorderBlock inc di - mov al,0ch ;select red color for block + mov al,0ch ;select red color for block call DrawBorderBlock loop DrawTopBorderLoop pop di @@ -527,10 +527,10 @@ DrawTopBorderLoop: mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2 DrawBottomBorderLoop: inc di - mov al,0ch ;select red color for block + mov al,0ch ;select red color for block call DrawBorderBlock inc di - mov al,0eh ;select yellow color for block + mov al,0eh ;select yellow color for block call DrawBorderBlock loop DrawBottomBorderLoop ret diff --git a/tests/pc/vga/L24-1.ASM b/tests/pc/vga/L24-1.ASM index 4de1a1272..141dc16bd 100644 --- a/tests/pc/vga/L24-1.ASM +++ b/tests/pc/vga/L24-1.ASM @@ -23,10 +23,10 @@ VERTICAL_BOX_WIDTH_IN_BYTES equ 10 ;width in bytes of the box used to ; ; VGA register equates. ; -GC_INDEX equ 3ceh ;GC index register -GC_ROTATE equ 3 ;GC data rotate/logical function - ; register index -GC_MODE equ 5 ;GC mode register index +GC_INDEX equ 3ceh ;GC index register +GC_ROTATE equ 3 ;GC data rotate/logical function + ; register index +GC_MODE equ 5 ;GC mode register index ; dseg segment para common 'DATA' ; @@ -40,10 +40,13 @@ LABEL_STRING_LENGTH equ $-LabelString ; FillPatternFF db 'Fill Pattern: 0FFh' FILL_PATTERN_FF_LENGTH equ $ - FillPatternFF + FillPattern00 db 'Fill Pattern: 000h' FILL_PATTERN_00_LENGTH equ $ - FillPattern00 + FillPatternVert db 'Fill Pattern: Vertical Bar' -FILL_PATTERN_VERT_LENGTH equ $ - FillPatternVert +FILL_PATTERN_VERT_LENGTH equ $ - FillPatternVert + FillPatternHorz db 'Fill Pattern: Horizontal Bar' FILL_PATTERN_HORZ_LENGTH equ $ - FillPatternHorz ; @@ -65,9 +68,9 @@ TEXT_UP macro TEXT_STRING, TEXT_LENGTH, ROW, COLUMN mov ah,13h ;BIOS write string function mov bp,offset TEXT_STRING ;ES:BP points to string mov cx,TEXT_LENGTH - mov dx,(ROW SHL 8) OR COLUMN ;position - sub al,al ;string is chars only, cursor not moved - mov bl,7 ;text attribute is white (light gray) + mov dx,(ROW SHL 8) OR COLUMN;position + sub al,al ;string is chars only, cursor not moved + mov bl,7 ;text attribute is white (light gray) int 10h endm ; @@ -90,19 +93,19 @@ start proc near ; Draw background of horizontal bars. ; mov dx,SCREEN_HEIGHT/4 - ;# of bars to draw (each 4 pixels high) - sub di,di ;start at offset 0 in display memory - mov ax,0ffffh ;fill pattern for light areas of bars + ;# of bars to draw (each 4 pixels high) + sub di,di ;start at offset 0 in display memory + mov ax,0ffffh ;fill pattern for light areas of bars mov bx,DEMO_AREA_WIDTH_IN_BYTES / 2 ;length of each bar mov si,SCREEN_WIDTH_IN_BYTES - DEMO_AREA_WIDTH_IN_BYTES mov bp,(SCREEN_WIDTH_IN_BYTES * 3) - DEMO_AREA_WIDTH_IN_BYTES BackgroundLoop: - mov cx,bx ;length of bar - rep stosw ;draw top half of bar - add di,si ;point to start of bottom half of bar - mov cx,bx ;length of bar - rep stosw ;draw bottom half of bar - add di,bp ;point to start of top of next bar + mov cx,bx ;length of bar + rep stosw ;draw top half of bar + add di,si ;point to start of bottom half of bar + mov cx,bx ;length of bar + rep stosw ;draw bottom half of bar + add di,bp ;point to start of top of next bar dec dx jnz BackgroundLoop ; @@ -204,12 +207,12 @@ ColumnLoop: ; DrawVerticalBox proc near DRAW_BOX_QUARTER 0ffh, VERTICAL_BOX_WIDTH_IN_BYTES - ;first fill pattern: solid fill + ;first fill pattern: solid fill DRAW_BOX_QUARTER 0, VERTICAL_BOX_WIDTH_IN_BYTES - ;second fill pattern: empty fill + ;second fill pattern: empty fill DRAW_BOX_QUARTER 033h, VERTICAL_BOX_WIDTH_IN_BYTES - ;third fill pattern: double-pixel - ; wide vertical bars + ;third fill pattern: double-pixel + ; wide vertical bars mov dx,DEMO_AREA_HEIGHT / 4 / 4 ;fourth fill pattern: horizontal bars in ; sets of 4 scan lines diff --git a/tests/pc/vga/L25-3.ASM b/tests/pc/vga/L25-3.ASM index 8f3984197..6452e233d 100644 --- a/tests/pc/vga/L25-3.ASM +++ b/tests/pc/vga/L25-3.ASM @@ -77,6 +77,7 @@ HorzBarLoop: SETGC GC_SET_RESET,01h ;set/reset value is 0ffh for plane 0 ; (the blue plane) and 0 for other ; planes + int 3 sub di,di mov cx,80*480 ;# bytes per screen mov al,0ffh ;since set/reset is enabled for all diff --git a/tests/pc/vga/README.md b/tests/pc/vga/README.md index e6dc90d03..195478a3b 100644 --- a/tests/pc/vga/README.md +++ b/tests/pc/vga/README.md @@ -19,7 +19,7 @@ I assume something similar was done on the CD-ROM that accompanied the Black Boo book or its CD-ROM, I'm extracting the source code directly from the Markdown text, and then "tabifying" it with 8-column tab stops. -Development of PCjs VGA support has just begun (June 2015), so don't expect *anything* here to to run properly yet. +Development of PCjs VGA support has just begun (June 2015), so don't expect everything here to to run properly yet. --- @@ -50,7 +50,7 @@ Since the contents of this particular directory will probably be in flux for a w Once things settle down, I'll generate a JSON-encoded disk image containing a snapshot of this directory, using the PCjs [DiskDump](/modules/diskdump/) module: - diskdump --dir=. --format=img --output=TESTVGA.img + diskdump --dir=. --format=img --output=TESTVGA.img --overwrite One advantage of using [DiskDump](/modules/diskdump/) is that it automatically converts linefeeds in known text files (including ASM files) into DOS-compatible CR/LF sequences.