1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-01 05:41:31 +00:00

changes to multisprite; moved inspect to vcs for now; local book links; z80 disasm

This commit is contained in:
Steven Hugg 2018-08-27 17:31:49 -04:00
parent 61d7860e12
commit 9d70c0fb9c
17 changed files with 391 additions and 222 deletions

View File

@ -156,7 +156,7 @@ We're always adding new things here, so subscribe to our mailing list for update
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-2">
<a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae" onclick="ga('send', 'event', 'books', 'click', 'vcs');"> <a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae" onclick="ga('send', 'event', 'books', 'click', 'vcs');">
<img class="img-responsive" border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=1541021304&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=pzp-20" ></a> <img class="img-responsive" border="0" src="./images/book_a2600_med.jpg" ></a>
<img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1541021304" /> <img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1541021304" />
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
@ -167,7 +167,7 @@ We're always adding new things here, so subscribe to our mailing list for update
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<a target="_blank" href="https://www.amazon.com/gp/product/1545484759/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1545484759&linkCode=as2&tag=pzp-20&linkId=2f3c42111bffe3466830939fff4053fc" onclick="ga('send', 'event', 'books', 'click', 'arcade');"> <a target="_blank" href="https://www.amazon.com/gp/product/1545484759/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1545484759&linkCode=as2&tag=pzp-20&linkId=2f3c42111bffe3466830939fff4053fc" onclick="ga('send', 'event', 'books', 'click', 'arcade');">
<img class="img-responsive" border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=1545484759&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=pzp-20" ></a> <img class="img-responsive" border="0" src="./images/book_arcade_med.jpg" ></a>
<img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1545484759" /> <img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1545484759" />
</div> </div>
<div class="col-md-4"> <div class="col-md-4">

View File

@ -56,8 +56,9 @@ TODO:
- VCS skips step on lsr/lsr after run to line - VCS skips step on lsr/lsr after run to line
- update window list after building 2nd time? - update window list after building 2nd time?
- stop debugging where mouse clicked - stop debugging where mouse clicked
- bring back the profiler!
- vcs disasm is broken
FYI: Image links for the books on http://8bitworkshop.com/ are broken
On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional? On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional?

BIN
images/book_a2600_med.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/book_arcade_med.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -309,6 +309,7 @@ function require(modname) {
<script src="gen/analysis.js"></script> <script src="gen/analysis.js"></script>
<script src="gen/audio.js"></script> <script src="gen/audio.js"></script>
<script src="gen/cpu/disasm6502.js"></script> <script src="gen/cpu/disasm6502.js"></script>
<script src="gen/cpu/disasmz80.js"></script>
<script src="gen/workertypes.js"></script> <script src="gen/workertypes.js"></script>
<script src="gen/project.js"></script> <script src="gen/project.js"></script>
<script src="gen/windows.js"></script> <script src="gen/windows.js"></script>

View File

@ -17,35 +17,6 @@ THREE_COPIES equ %011 ; for NUSIZ registers
seg Code seg Code
; setup 6-digit score
Digit6Setup: subroutine
lda #$18
sta COLUP0
lda #$28
sta COLUP1
lda #THREE_COPIES
sta NUSIZ0
sta NUSIZ1
; set horizontal position of player objects
sta WSYNC
SLEEP 26
sta RESP0
sta RESP1
lda #$10
sta HMP1
sta WSYNC
sta HMOVE
sta HMCLR
lda #0
sta REFP0
sta REFP1
sta GRP0
sta GRP1
lda #1
sta VDELP0
sta VDELP1
rts
; Adds value to 6-BCD-digit score. ; Adds value to 6-BCD-digit score.
; A = 1st BCD digit ; A = 1st BCD digit
; X = 2nd BCD digit ; X = 2nd BCD digit
@ -97,13 +68,40 @@ GetDigitPtrs: subroutine
; Display the resulting 48x8 bitmap ; Display the resulting 48x8 bitmap
; using the Digit0-5 pointers. ; using the Digit0-5 pointers.
; A = color of scoreboard
DrawDigits: subroutine DrawDigits: subroutine
.temp equ Temp1 .temp equ Temp1
.count equ Temp2 .count equ Temp2
sta WSYNC ; clear player bitmaps first
SLEEP 40 ; start near end of scanline jsr ClearGRP
lda #7 ; set colors
sta .count sta COLUP0
sta COLUP1
; set horizontal position of player objects
sta WSYNC
; instead of SLEEP 26...
lda #THREE_COPIES
sta NUSIZ0
sta NUSIZ1
lda #1
sta VDELP0
sta VDELP1
lda #$10
sta HMP1
stx HMP0 ; x = 0
nop
; set player positions (26 cycles later)
stx RESP0
stx RESP1
stx REFP0
stx REFP1
; apply fine adjustment
sta WSYNC
sta HMOVE
sta HMCLR
lda #8
sta .count ; 8 scanlines
jsr ClearGRP ; just killing some time...
.bigloop .bigloop
ldy .count ; counts backwards ldy .count ; counts backwards
lda (Digit0),y ; load B0 (1st sprite byte) lda (Digit0),y ; load B0 (1st sprite byte)
@ -119,18 +117,19 @@ DrawDigits: subroutine
tax ; -> X tax ; -> X
lda (Digit3),y ; load B3 -> A lda (Digit3),y ; load B3 -> A
ldy .temp ; load B5 -> Y ldy .temp ; load B5 -> Y
.storegrp
sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0 sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0
stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1 stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1
sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0 sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0
sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1 sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1
dec .count ; go to next line dec .count ; go to next line
bpl .bigloop ; repeat until < 0 bpl .bigloop ; repeat until < 0
ClearGRP
lda #0 ; clear the sprite registers ldx #0 ; empty out player bitmaps
sta GRP0 stx GRP0
sta GRP1 stx GRP1
sta GRP0 stx GRP0
sta GRP1 stx GRP1
rts rts
; Font table for digits 0-9 (8x8 pixels) ; Font table for digits 0-9 (8x8 pixels)
@ -141,4 +140,4 @@ Digit6Font:
hex 0006067f661e0e06003c6606067c607e hex 0006067f661e0e06003c6606067c607e
hex 003c66667c60663c00181818180c667e hex 003c66667c60663c00181818180c667e
hex 003c66663c66663c003c66063e66663c hex 003c66663c66663c003c66063e66663c
hex 00 ; so top of 9 is clean

View File

@ -40,7 +40,7 @@ InitLoop
sta YPos0,x sta YPos0,x
sta Flags0,x sta Flags0,x
clc clc
adc #13 adc #23
iny iny
iny iny
iny iny
@ -68,6 +68,9 @@ InitLoop
sta PF1Ptr+1 sta PF1Ptr+1
lda #>Start+1 lda #>Start+1
sta PF2Ptr+1 sta PF2Ptr+1
lda YPos0+1
adc #9
sta YPos0+1
; Next frame loop ; Next frame loop
NextFrame NextFrame
@ -76,12 +79,12 @@ NextFrame
; Do joystick movement ; Do joystick movement
ldy #1 ldy #1
jsr MoveJoystick jsr MoveJoystick
jsr Digit6Setup
jsr GetDigitPtrs jsr GetDigitPtrs
jsr MSpritePre jsr MSpritePre
TIMER_WAIT TIMER_WAIT
; end of VBLANK ; end of VBLANK
; Needed for single-line kernel
TIMER_TABLE_SETUP TIMER_TABLE_SETUP
; Scoreboard ; Scoreboard
jsr DrawDigits jsr DrawDigits
@ -90,7 +93,6 @@ NextFrame
sta COLUBK sta COLUBK
lda #22 lda #22
jsr MSpriteDraw2 jsr MSpriteDraw2
; Overscan ; Overscan
TIMER_SETUP 28 TIMER_SETUP 28
; Clear all colors to black before overscan ; Clear all colors to black before overscan
@ -99,6 +101,11 @@ NextFrame
stx COLUP0 stx COLUP0
stx COLUP1 stx COLUP1
stx COLUPF stx COLUPF
; add score
ldy #0
ldx #0
lda #1
jsr AddScore
; 30-2 lines of overscan ; 30-2 lines of overscan
TIMER_WAIT TIMER_WAIT
; Go to next frame ; Go to next frame

View File

@ -45,16 +45,20 @@
; MSpriteDraw1 - Single-line sprites, no playfield. ; MSpriteDraw1 - Single-line sprites, no playfield.
; This kernel requires TIMER_TABLE_SETUP at the 0th ; This kernel requires TIMER_TABLE_SETUP at the 0th
; scanline, as it uses the timer to figure out the ; scanline, as it uses the timer to figure out the
; current scanline. ; current scanline. No vertical clipping is performed.
; In fact, if sprites go past the bottom, it will
; mess up the vertical sync.
; MSpriteDraw2 - Double-line sprites with playfield. ; MSpriteDraw2 - Double-line sprites with playfield.
; The playfield is updated and the sprites are positioned ; The playfield is updated and the sprites are positioned
; in 8-scanline segments. ; in 8-scanline segments.
; This kernel uses the timer internally, but does not ; This kernel uses the timer internally, but does not
; use the timer table or TIMER_TABLE_SETUP. ; use the timer table or TIMER_TABLE_SETUP.
; Sprites are clipped on the bottom edge, but disappear
; off the top edge.
; Your program must call MSpriteInit when it starts, ; Your program must call MSpriteInit when it starts,
; and MSpritePre between every frame. ; and MSpriteFrame between every frame.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -119,7 +123,7 @@ MSpriteInit subroutine
rts rts
; call between frames ; call between frames
MSpritePre subroutine MSpriteFrame subroutine
; Do one iteration of bubble sort on sprite indices ; Do one iteration of bubble sort on sprite indices
ldx #NSprites-2 ldx #NSprites-2
.SortLoop .SortLoop
@ -127,20 +131,18 @@ MSpritePre subroutine
dex dex
bpl .SortLoop ; loop until <= 0 bpl .SortLoop ; loop until <= 0
; Reset scanline counter and sprite objects ; Reset scanline counter and sprite objects
ldx #0 jsr ResetCounters
stx CurIndex stx CurIndex
stx SIndx0
stx SIndx1
stx SSize0
stx SSize1
rts rts
; single-line kernel routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SINGLE-LINE KERNEL
MSpriteDraw1 subroutine MSpriteDraw1 subroutine
.NextFindSprite .NextFindSprite
; Try to schedule sprites to both players ; Try to schedule sprites to both players
jsr FindAnotherSprite jsr FindAnotherSprite
jsr FindAnotherSprite
; Apply fine offsets ; Apply fine offsets
sta WSYNC ; start next scanline sta WSYNC ; start next scanline
sta HMOVE ; apply the previous fine position(s) sta HMOVE ; apply the previous fine position(s)
@ -157,6 +159,106 @@ MSpriteDraw1 subroutine
lda #191 lda #191
jmp WaitForScanline jmp WaitForScanline
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DOUBLE-LINE KERNEL WITH BACKGROUND
MSpritePrefill2 subroutine
lda #0
sta Scanline
jsr FindAnotherSprite2
jsr FindAnotherSprite2
sta WSYNC
sta HMOVE ; apply the previous fine position(s)
sta HMCLR ; clear motion registers
rts
MSpriteDraw2 subroutine
sta WSYNC
sta PFIndex ; 24 * 4 scanlines = 96 2xlines
lda #0
sta PFCount
sta VDELP1
lda #1
sta VDELP0 ; updates to GRP0 will be delayed
jmp .NewSprites
.Draw8Lines
; Phase 0: Fetch PF0 byte
jsr DrawSprites2
ldy PFIndex
lda (PF0Ptr),y ; load PF0
sta tmpPF0
; Phase 1: Fetch PF1 byte
jsr DrawSprites2
ldy PFIndex
lda (PF1Ptr),y ; load PF1
sta tmpPF1
; Phase 2: Fetch PF2 byte
jsr DrawSprites2
ldy PFIndex
lda (PF2Ptr),y ; load PF2
sty PFIndex
sta tmpPF2
; Phase 3: Write PF0/PF1/PF2 registers
jsr DrawSprites2
lda tmpPF0
sta PF0
lda tmpPF1
sta PF1
lda tmpPF2
sta PF2
; Go to next scanline, unless playfield is done
; or unless this segment is done
dec PFIndex
bmi .NoMoreLines ; playfield done
dec PFCount
bpl .Draw8Lines ; keep drawing
; done drawing, reset player counters
jsr ResetCounters
.NewSprites
lda PFIndex
asl
asl
eor #$7f
sbc #34
sta Scanline ; Scanlines = 127 - PFIndex*4
; Set up 0-2 player objects taking up to 8 scanlines
TIMER_SETUP 7
jsr FindAnotherSprite2
jsr CalcSpriteEnd
; Update playfield
ldy PFIndex
lda (PF0Ptr),y ; load PF0 -> X
tax
lda (PF1Ptr),y ; load PF1 -> tmp
sta tmpPF1
lda (PF2Ptr),y ; load PF2 -> Y
tay
; Apply fine offsets
TIMER_WAIT ; wait for 8th scanlines and WSYNC
sta HMOVE ; apply the previous fine position(s)
sta HMCLR ; clear motion registers
; Store playfield registers
stx PF0
lda tmpPF1
sta PF1
sty PF2
dec PFIndex ; no more playfield?
bmi .NoMoreLines
lda PFCount
bne .Draw8Lines
sta WSYNC ; one more line
beq .NewSprites
.NoMoreLines
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SPRITE DRAWING ROUTINES
; used by kernels
; need to set SIndx0/1, SSize0/1, PData0/1, PColr0/1
; all players must already be setup
; called by single-line kernel ; called by single-line kernel
DrawSprites1 subroutine DrawSprites1 subroutine
; Wait for next precise scanline ; Wait for next precise scanline
@ -166,6 +268,7 @@ DrawSprites1 subroutine
lda Timer2Scanline,y ; lookup scanline # lda Timer2Scanline,y ; lookup scanline #
beq .AnotherScanline ; not if zero! beq .AnotherScanline ; not if zero!
sta Scanline ; save it sta Scanline ; save it
DrawSprites1a
; Calculate # of lines to draw for each sprite ; Calculate # of lines to draw for each sprite
; Sprite Y - current scanline + sprite height ; Sprite Y - current scanline + sprite height
lda SIndx0 lda SIndx0
@ -193,6 +296,7 @@ DrawSprites1 subroutine
tax ; X = # of lines left to draw tax ; X = # of lines left to draw
beq .NoSprites ; X = 0? we're done beq .NoSprites ; X = 0? we're done
sta WSYNC ; next scanline sta WSYNC ; next scanline
DrawSprites1b
.DrawNextScanline .DrawNextScanline
; Make sure player 0 index is within bounds ; Make sure player 0 index is within bounds
ldy SIndx0 ldy SIndx0
@ -227,10 +331,7 @@ DrawSprites1 subroutine
dex dex
bne .DrawNextScanline bne .DrawNextScanline
; Free up both player objects by zeroing them out ; Free up both player objects by zeroing them out
stx SIndx0 jsr ResetCounters
stx SIndx1
stx SSize0
stx SSize1
sta WSYNC sta WSYNC
stx GRP0 stx GRP0
stx GRP1 stx GRP1
@ -245,86 +346,41 @@ DrawSprites1 subroutine
sta COLUP0 sta COLUP0
beq .DrawSprite1 ; always taken due to lda #0 beq .DrawSprite1 ; always taken due to lda #0
MSpriteDraw2 subroutine ; called by 2-line kernel
sta WSYNC DrawSprites2 subroutine
sta PFIndex ; 24 * 4 scanlines = 96 2xlines ; Fetch sprite 0 values
lda #0 lda SSize0 ; height in 2xlines
sta PFCount sec
sta VDELP1 isb SIndx0 ; INC yp0, then SBC yp0
lda #1 bcs DoDraw0 ; inside bounds?
sta VDELP0 ; updates to GRP0 will be delayed lda #0 ; no, load the padding offset (0)
.KernelLoop DoDraw0
dec PFCount tay ; -> Y
bmi .NewSprites lda (PColr0),y ; color for both lines
.Draw8Lines sta Colp0 ; -> colp0
; Phase 0: Fetch PF0 byte lda (PData0),y ; bitmap for first line
jsr DrawSprites2 sta GRP0 ; -> [GRP0] (delayed due to VDEL)
ldy PFIndex ; Fetch sprite 1 values
lda (PF0Ptr),y ; load PF0 lda SSize1 ; height in 2xlines
sta tmpPF0 sec
; Phase 1: Fetch PF1 byte isb SIndx1 ; INC yp0, then SBC yp0
jsr DrawSprites2 bcs DoDraw1 ; inside bounds?
ldy PFIndex lda #0 ; no, load the padding offset (0)
lda (PF1Ptr),y ; load PF1 DoDraw1
sta tmpPF1 tay ; -> Y
; Phase 2: Fetch PF2 byte lda (PColr1),y ; color for both lines
jsr DrawSprites2
ldy PFIndex
lda (PF2Ptr),y ; load PF2
sty PFIndex
sta tmpPF2
; Phase 3: Write PF0/PF1/PF2 registers
jsr DrawSprites2
lda tmpPF0
sta PF0
lda tmpPF1
sta PF1
lda tmpPF2
sta PF2
; Go to next scanline, unless playfield is done
dec PFIndex
bpl .KernelLoop
.NoMoreLines
rts
.NewSprites
; lda PFIndex
; sta COLUBK
lda PFIndex
asl
asl
eor #$7f
sbc #34
sta Scanline ; Scanlines = 127 - PFIndex*4
; Set up 0-2 player objects taking up to 8 scanlines
TIMER_SETUP 8
ldx #0
stx SSize0
stx SSize1
stx SIndx0
stx SIndx1
jsr FindAnotherSprite2
; jsr FindAnotherSprite2
jsr CalcSpriteEnd
; Update playfield
ldy PFIndex
lda (PF0Ptr),y ; load PF0 -> X
tax tax
lda (PF1Ptr),y ; load PF1 -> tmp lda (PData1),y ; bitmap for first line
sta tmpPF1
lda (PF2Ptr),y ; load PF2 -> Y
tay tay
; Apply fine offsets ; WSYNC and store sprite values
TIMER_WAIT ; wait for 8th scanlines and WSYNC lda Colp0
sta HMOVE ; apply the previous fine position(s) ; still have about 30 cycles left...
sta HMCLR ; clear motion registers sta WSYNC
; Store playfield registers sty GRP1 ; GRP0 is also updated due to VDELP0 flag
stx PF0 stx COLUP1
lda tmpPF1 sta COLUP0
sta PF1 ; Return to caller
sty PF2 rts
dec PFIndex ; no more playfield?
bpl .KernelLoop
jmp .NoMoreLines
CalcSpriteEnd subroutine CalcSpriteEnd subroutine
; Calculate # of lines to draw for each sprite ; Calculate # of lines to draw for each sprite
@ -355,16 +411,24 @@ CalcSpriteEnd subroutine
; Compute the number of 8x lines in this section ; Compute the number of 8x lines in this section
eor #$ff eor #$ff
clc clc
adc #5 adc #1
lsr lsr
lsr lsr
sta PFCount sta PFCount
rts rts
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; COMMON ROUTINES
;;; ; COMMON ROUTINES
ResetCounters subroutine
ldx #0
stx SSize0
stx SSize1
stx SIndx0
stx SIndx1
rts
; We were too late to display a sprite. ; We were too late to display a sprite.
; Put it earlier in the sort order and try next frame. ; Put it earlier in the sort order and try next frame.
; X = sort index ; X = sort index
@ -406,6 +470,7 @@ FindAnotherSprite2 ; alternate entry point when scanline known
; Due to timing issues, we have artifacts if player 1 is ; Due to timing issues, we have artifacts if player 1 is
; too close to the left edge of the screen. So we'd prefer to ; too close to the left edge of the screen. So we'd prefer to
; put those sprites in the player 0 slot. ; put those sprites in the player 0 slot.
; TODO: disable for 2-line sprite kernel
cmp #34 ; X < 34 cmp #34 ; X < 34
bcc .Plyr1NotReady bcc .Plyr1NotReady
; First let's set its horizontal offset ; First let's set its horizontal offset
@ -436,7 +501,7 @@ FindAnotherSprite2 ; alternate entry point when scanline known
ldy #0 ldy #0
lda (PColr1),y lda (PColr1),y
sta SSize1 sta SSize1
inc SSize1 ; +1 to size inc SSize1 ; +1 to height
jmp .SetupDone jmp .SetupDone
.Plyr1NotReady .Plyr1NotReady
ldx SIndx0 ldx SIndx0
@ -465,47 +530,12 @@ FindAnotherSprite2 ; alternate entry point when scanline known
ldy #0 ldy #0
lda (PColr0),y lda (PColr0),y
sta SSize0 sta SSize0
inc SSize0 ; +1 to size inc SSize0 ; +1 to height
.SetupDone .SetupDone
inc CurIndex ; go to next sprite in sort order inc CurIndex ; go to next sprite in sort order
.NoNearSprite .NoNearSprite
rts rts
; called by 2-line kernel
DrawSprites2 subroutine
; Fetch sprite 0 values
lda SSize0 ; height in 2xlines
sec
isb SIndx0 ; INC yp0, then SBC yp0
bcs DoDraw0 ; inside bounds?
lda #0 ; no, load the padding offset (0)
DoDraw0
tay ; -> Y
lda (PColr0),y ; color for both lines
sta Colp0 ; -> colp0
lda (PData0),y ; bitmap for first line
sta GRP0 ; -> [GRP0] (delayed due to VDEL)
; Fetch sprite 1 values
lda SSize1 ; height in 2xlines
sec
isb SIndx1 ; INC yp0, then SBC yp0
bcs DoDraw1 ; inside bounds?
lda #0 ; no, load the padding offset (0)
DoDraw1
tay ; -> Y
lda (PColr1),y ; color for both lines
tax
lda (PData1),y ; bitmap for first line
tay
; WSYNC and store sprite values
lda Colp0
sta WSYNC
sty GRP1 ; GRP0 is also updated due to VDELP0 flag
stx COLUP1
sta COLUP0
; Return to caller
rts
; Perform one sort iteration ; Perform one sort iteration
; X register contains sort index (0 to NSprites-1) ; X register contains sort index (0 to NSprites-1)
SwapSprites subroutine SwapSprites subroutine

View File

@ -0,0 +1,40 @@

`include "hvsync_generator.v"
/*
Player 1 Keys: arrow keys + space + shift
Player 2 Keys: A/D/W/S + Z + X
*/
module switches_top(clk, reset, hsync, vsync,
switches_p1, switches_p2,
rgb);
input clk, reset;
input [7:0] switches_p1;
input [7:0] switches_p2;
output hsync, vsync;
output [2:0] rgb;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
// select p1 bit based on vertical position
wire p1gfx = switches_p1[vpos[7:5]];
// select p2 bit based on horizontal position
wire p2gfx = switches_p2[hpos[7:5]];
assign rgb = {1'b0,
display_on && p1gfx,
display_on && p2gfx};
endmodule

View File

@ -2,6 +2,8 @@
import { RAM, RasterVideo, dumpRAM, lookupSymbol } from "./emu"; import { RAM, RasterVideo, dumpRAM, lookupSymbol } from "./emu";
import { hex } from "./util"; import { hex } from "./util";
import { CodeAnalyzer } from "./analysis"; import { CodeAnalyzer } from "./analysis";
import { disassemble6502 } from "./cpu/disasm6502";
import { disassembleZ80 } from "./cpu/disasmz80";
declare var Z80_fast, jt, CPU6809; declare var Z80_fast, jt, CPU6809;
@ -523,6 +525,9 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform {
} }
} }
} }
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
return disassembleZ80(pc, read(pc), read(pc+1), read(pc+2), read(pc+3));
}
} }
export function getToolForFilename_z80(fn) { export function getToolForFilename_z80(fn) {

View File

@ -1,4 +1,6 @@
import { hex } from "../util";
var OPS_6502 = [ var OPS_6502 = [
{mn:"BRK",am:"",nb:1,il:0,c1:7,c2:0}, // 00 {mn:"BRK",am:"",nb:1,il:0,c1:7,c2:0}, // 00
{mn:"ORA",am:"(aa,x)",nb:2,il:0,c1:6,c2:0}, // 01 {mn:"ORA",am:"(aa,x)",nb:2,il:0,c1:6,c2:0}, // 01
@ -258,24 +260,7 @@ var OPS_6502 = [
{mn:"ISB",am:"AAAA,x",nb:3,il:1,c1:7,c2:1}, // FF {mn:"ISB",am:"AAAA,x",nb:3,il:1,c1:7,c2:1}, // FF
]; ];
function disassemble6502(pc:number, b0:number, b1:number, b2:number) : {line:string, nbytes:number} { export function disassemble6502(pc:number, b0:number, b1:number, b2:number) : {line:string, nbytes:number} {
function formatHex(number, len) {
if (typeof number === "undefined" || number === null || isNaN(number)) {
throw new Error("Invalid value \"" + number + "\" passed to formatHex()");
}
var str = number.toString(16).toUpperCase();
if (!len) {
if (str.length % 2 == 1) {
len = str.length+1;
}
}
while (str.length < len) {
str = "0" + str;
}
return str;
}
var op = OPS_6502[b0]; var op = OPS_6502[b0];
var s = op.mn; var s = op.mn;
@ -283,10 +268,10 @@ function disassemble6502(pc:number, b0:number, b1:number, b2:number) : {line:str
if (am == 'branch') { if (am == 'branch') {
var offset = (b1 < 0x80) ? (pc+2+b1) : (pc+2-(256-b1)); var offset = (b1 < 0x80) ? (pc+2+b1) : (pc+2-(256-b1));
offset &= 0xffff; offset &= 0xffff;
am = '$'+formatHex(offset, 4); am = '$'+hex(offset, 4);
} else { } else {
am = am.replace('aa','$'+formatHex(b1, 2)); am = am.replace('aa','$'+hex(b1, 2));
am = am.replace('AAAA','$'+formatHex(b1+(b2<<8), 4)); am = am.replace('AAAA','$'+hex(b1+(b2<<8), 4));
} }
return {line:op.mn + " " + am, nbytes:op.nb}; return {line:op.mn + " " + am, nbytes:op.nb};
}; };

72
src/cpu/disasmz80.ts Normal file
View File

@ -0,0 +1,72 @@
import { hex } from "../util";
const Z80_OPS = ["nop","ld bc,xx","ld (bc),a","inc bc","inc b","dec b","ld b,x","rlca","ex af,af'","add hl,bc","ld a,(bc)","dec bc","inc c","dec c","ld c,x","rrca","djnz x","ld de,xx","ld (de),a","inc de","inc d","dec d","ld d,x","rla","jr x","add hl,de","ld a,(de)","dec de","inc e","dec e","ld e,x","rra","jr nz,x","ld hl,xx","ld (xx),hl","inc hl","inc h","dec h","ld h,x","daa","jr z,x","add hl,hl","ld hl,(xx)","dec hl","inc l","dec l","ld l,x","cpl","jr nc,x","ld sp,xx","ld (xx),a","inc sp","inc (hl)","dec (hl)","ld (hl),x","scf","jr c,x","add hl,sp","ld a,(xx)","dec sp","inc a","dec a","ld a,x","ccf","ld b,b","ld b,c","ld b,d","ld b,e","ld b,h","ld b,l","ld b,(hl)","ld b,a","ld c,b","ld c,c","ld c,d","ld c,e","ld c,h","ld c,l","ld c,(hl)","ld c,a","ld d,b","ld d,c","ld d,d","ld d,e","ld d,h","ld d,l","ld d,(hl)","ld d,a","ld e,b","ld e,c","ld e,d","ld e,e","ld e,h","ld e,l","ld e,(hl)","ld e,a","ld h,b","ld h,c","ld h,d","ld h,e","ld h,h","ld h,l","ld h,(hl)","ld h,a","ld l,b","ld l,c","ld l,d","ld l,e","ld l,h","ld l,l","ld l,(hl)","ld l,a","ld (hl),b","ld (hl),c","ld (hl),d","ld (hl),e","ld (hl),h","ld (hl),l","","ld (hl),a","ld a,b","ld a,c","ld a,d","ld a,e","ld a,h","ld a,l","ld a,(hl)","ld a,a","add a,b","add a,c","add a,d","add a,e","add a,h","add a,l","add a,(hl)","add a,a","adc a,b","adc a,c","adc a,d","adc a,e","adc a,h","adc a,l","adc a,(hl)","adc a,a","sub b","sub c","sub d","sub e","sub h","sub l","sub (hl)","sub a","sbc a,b","sbc a,c","sbc a,d","sbc a,e","sbc a,h","sbc a,l","sbc a,(hl)","sbc a,a","and b","and c","and d","and e","and h","and l","and (hl)","and a","xor b","xor c","xor d","xor e","xor h","xor l","xor (hl)","xor a","or b","or c","or d","or e","or h","or l","or (hl)","or a","cp b","cp c","cp d","cp e","cp h","cp l","cp (hl)","cp a","ret nz","pop bc","jp nz,xx","jp xx","call nz,xx","push bc","add a,x","rst 00h","ret z","","jp z,xx","xxBITxx","call z,xx","call xx","adc a,x","rst 08h","ret nc","pop de","jp nc,xx","out (x),a","call nc,xx","push de","sub x","rst 10h","ret c","exx","jp c,xx","in a,(x)","call c,xx","xxIXxx","sbc a,x","rst 18h","ret po","pop hl","jp po,xx","ex (sp),hl","call po,xx","push hl","and x","rst 20h","ret pe","jp (hl)","jp pe,xx","ex de,hl","call pe,xx","xx80xx","xor x","rst 28h","ret p","pop af","jp p,xx","di","call p,xx","push af","or x","rst 30h","ret m","ld sp,hl","jp m,xx","ei","call m,xx","xxIYxx","cp x","rst 38h"];
const Z80_OPS_ED = ["in b,(c)","out (c),b","sbc hl,bc","ld (xx),bc","neg","retn","im 0","ld i,a","in c,(c)","out (c),c","adc hl,bc","ld bc,(xx)","neg","reti","","ld r,a","in d,(c)","out (c),d","sbc hl,de","ld (xx),de","neg","retn","im 1","ld a,i","in e,(c)","out (c),e","adc hl,de","ld de,(xx)","neg","retn","im 2","ld a,r","in h,(c)","out (c),h","sbc hl,hl","ld (xx),hl","neg","retn","","rrd","in l,(c)","out (c),l","adc hl,hl","ld hl,(xx)","neg","retn","","rld","in f,(c)","out (c),f","sbc hl,sp","ld (xx),sp","neg","retn","","","in a,(c)","out (c),a","adc hl,sp","ld sp,(xx)","neg","reti","","","ldi","cpi","ini","outi","","","","","ldd","cpd","ind","outd","","","","","ldir","cpir","inir","otir","","","","","lddr","cpdr","indr","otdr","","","",""];
const Z80_OPS_CB = ["rlc b","rlc c","rlc d","rlc e","rlc h","rlc l","rlc (hl)","rlc a","rrc b","rrc c","rrc d","rrc e","rrc h","rrc l","rrc (hl)","rrc a","rl b","rl c","rl d","rl e","rl h","rl l","rl (hl)","rl a","rr b","rr c","rr d","rr e","rr h","rr l","rr (hl)","rr a","sla b","sla c","sla d","sla e","sla h","sla l","sla (hl)","sla a","sra b","sra c","sra d","sra e","sra h","sra l","sra (hl)","sra a","sll b","sll c","sll d","sll e","sll h","sll l","sll (hl)","sll a","srl b","srl c","srl d","srl e","srl h","srl l","srl (hl)","srl a","bit 0,b","bit 0,c","bit 0,d","bit 0,e","bit 0,h","bit 0,l","bit 0,(hl)","bit 0,a","bit 1,b","bit 1,c","bit 1,d","bit 1,e","bit 1,h","bit 1,l","bit 1,(hl)","bit 1,a","bit 2,b","bit 2,c","bit 2,d","bit 2,e","bit 2,h","bit 2,l","bit 2,(hl)","bit 2,a","bit 3,b","bit 3,c","bit 3,d","bit 3,e","bit 3,h","bit 3,l","bit 3,(hl)","bit 3,a","bit 4,b","bit 4,c","bit 4,d","bit 4,e","bit 4,h","bit 4,l","bit 4,(hl)","bit 4,a","bit 5,b","bit 5,c","bit 5,d","bit 5,e","bit 5,h","bit 5,l","bit 5,(hl)","bit 5,a","bit 6,b","bit 6,c","bit 6,d","bit 6,e","bit 6,h","bit 6,l","bit 6,(hl)","bit 6,a","bit 7,b","bit 7,c","bit 7,d","bit 7,e","bit 7,h","bit 7,l","bit 7,(hl)","bit 7,a","res 0,b","res 0,c","res 0,d","res 0,e","res 0,h","res 0,l","res 0,(hl)","res 0,a","res 1,b","res 1,c","res 1,d","res 1,e","res 1,h","res 1,l","res 1,(hl)","res 1,a","res 2,b","res 2,c","res 2,d","res 2,e","res 2,h","res 2,l","res 2,(hl)","res 2,a","res 3,b","res 3,c","res 3,d","res 3,e","res 3,h","res 3,l","res 3,(hl)","res 3,a","res 4,b","res 4,c","res 4,d","res 4,e","res 4,h","res 4,l","res 4,(hl)","res 4,a","res 5,b","res 5,c","res 5,d","res 5,e","res 5,h","res 5,l","res 5,(hl)","res 5,a","res 6,b","res 6,c","res 6,d","res 6,e","res 6,h","res 6,l","res 6,(hl)","res 6,a","res 7,b","res 7,c","res 7,d","res 7,e","res 7,h","res 7,l","res 7,(hl)","res 7,a","set 0,b","set 0,c","set 0,d","set 0,e","set 0,h","set 0,l","set 0,(hl)","set 0,a","set 1,b","set 1,c","set 1,d","set 1,e","set 1,h","set 1,l","set 1,(hl)","set 1,a","set 2,b","set 2,c","set 2,d","set 2,e","set 2,h","set 2,l","set 2,(hl)","set 2,a","set 3,b","set 3,c","set 3,d","set 3,e","set 3,h","set 3,l","set 3,(hl)","set 3,a","set 4,b","set 4,c","set 4,d","set 4,e","set 4,h","set 4,l","set 4,(hl)","set 4,a","set 5,b","set 5,c","set 5,d","set 5,e","set 5,h","set 5,l","set 5,(hl)","set 5,a","set 6,b","set 6,c","set 6,d","set 6,e","set 6,h","set 6,l","set 6,(hl)","set 6,a","set 7,b","set 7,c","set 7,d","set 7,e","set 7,h","set 7,l","set 7,(hl)","set 7,a"];
// TODO: DD, etc -- http://z80-heaven.wikidot.com/opcode-reference-chart
export function disassembleZ80(pc:number, b0:number, b1:number, b2:number, b3:number) : {line:string, nbytes:number} {
var op,n,am;
var bytes = [b0,b1,b2,b3];
n=1;
switch (b0) {
case 0xcb:
am = Z80_OPS_CB[b1];
n++;
break;
case 0xed:
if (b1 >= 0x40 && b1 <= 0x7f) am = Z80_OPS_ED[b1 - 0x40];
if (b1 >= 0xa0 && b1 <= 0xbf) am = Z80_OPS_ED[b1 - 0xa0 + 0x40];
n++;
break;
case 0xdd:
am = Z80_OPS[b1].replace(/\bhl\b/, 'ix');
n++;
break;
default:
am = Z80_OPS[b0];
break;
}
if (!am || !am.length) am = "??";
if (am.indexOf('xx') >= 0) {
am = am.replace(/\bxx\b/,'$'+hex(bytes[n]+(bytes[n+1]<<8), 4));
n += 2;
} else if (am.indexOf('x') >= 0) {
if (am.startsWith('j')) {
var offset = (b1 < 0x80) ? (pc+2+b1) : (pc+2-(256-b1));
offset &= 0xffff;
am = am.replace(/\bx\b/,'$'+hex(offset, 4));
} else {
am = am.replace(/\bx\b/,'$'+hex(bytes[n], 2));
}
n += 1;
}
/*
if (b0 == 0xcd && (s = cb_prefixed_ops[b1])) {
n = 2;
} else if (s = one_byte_instructions[b0]) {
n = 1;
} else if (s = two_byte_instructions[b0]) {
n = 2;
} else if (s = three_byte_instructions[b0]) {
n = 3;
} else {
s = "??";
n = 1;
}
/*
TODO: http://z80-heaven.wikidot.com/opcode-reference-chart
if (am == 'branch') {
var offset = (b1 < 0x80) ? (pc+2+b1) : (pc+2-(256-b1));
offset &= 0xffff;
am = '$'+formatHex(offset, 4);
} else {
am = am.replace('aa','$'+formatHex(b1, 2));
am = am.replace('AAAA','$'+formatHex(b1+(b2<<8), 4));
}
*/
return {line:am.toUpperCase(), nbytes:n};
};

View File

@ -4,11 +4,16 @@ import { Platform, BasePlatform, cpuStateToLongString_6502, BaseMAMEPlatform, Em
import { PLATFORMS, RAM, newAddressDecoder, dumpRAM } from "../emu"; import { PLATFORMS, RAM, newAddressDecoder, dumpRAM } from "../emu";
import { hex, lpad, tobin, byte2signed } from "../util"; import { hex, lpad, tobin, byte2signed } from "../util";
import { CodeAnalyzer_vcs } from "../analysis"; import { CodeAnalyzer_vcs } from "../analysis";
import { disassemble6502 } from "../cpu/disasm6502";
declare var platform : Platform; // global platform object declare var platform : Platform; // global platform object
declare var Javatari : any; declare var Javatari : any;
declare var jt : any; // 6502 declare var jt : any; // 6502
// TODO: import or put in platform
declare var symbolmap : {[ident:string]:number};
declare var addr2symbol : {[addr:number]:string};
const VCS_PRESETS = [ const VCS_PRESETS = [
{id:'examples/hello', chapter:4, name:'Hello 6502 and TIA'}, {id:'examples/hello', chapter:4, name:'Hello 6502 and TIA'},
{id:'examples/vsync', chapter:5, name:'Painting on the CRT', title:'Color Bars'}, {id:'examples/vsync', chapter:5, name:'Painting on the CRT', title:'Color Bars'},
@ -38,7 +43,7 @@ const VCS_PRESETS = [
{id:'examples/wavetable', chapter:36, name:'Wavetable Sound'}, {id:'examples/wavetable', chapter:36, name:'Wavetable Sound'},
{id:'examples/fracpitch', name:'Fractional Pitch'}, {id:'examples/fracpitch', name:'Fractional Pitch'},
{id:'examples/pal', name:'PAL Video Output'}, {id:'examples/pal', name:'PAL Video Output'},
{id:'examples/testlibrary', name:'VCS Library Demo'}, // {id:'examples/testlibrary', name:'VCS Library Demo'},
// {id:'examples/music2', name:'Pitch-Accurate Music'}, // {id:'examples/music2', name:'Pitch-Accurate Music'},
// {id:'examples/fullgame', name:'Thru Hike: The Game', title:'Thru Hike'}, // {id:'examples/fullgame', name:'Thru Hike: The Game', title:'Thru Hike'},
]; ];
@ -246,6 +251,22 @@ class VCSPlatform extends BasePlatform {
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine { disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
return disassemble6502(pc, read(pc), read(pc+1), read(pc+2)); return disassemble6502(pc, read(pc), read(pc+1), read(pc+2));
} }
inspect(ident : string) : string {
if (this.isRunning()) return; // only inspect when stopped
var result;
var addr = symbolmap && (symbolmap[ident]); // || symbolmap['_'+ident]);
if (addr >= 0x80 && addr < 0x100) { // in RAM?
var size=4;
result = "$" + hex(addr,4) + ":";
for (var i=0; i<size; i++) {
var byte = platform.readAddress(addr+i);
result += " $" + hex(byte,2) + " (" + byte + ")";
if (addr2symbol[addr+1]) break; // stop if we hit another symbol
else if (i==size-1) result += " ...";
}
}
return result;
}
}; };
// TODO: mixin for Base6502Platform? // TODO: mixin for Base6502Platform?

View File

@ -112,19 +112,6 @@ export class SourceEditor implements ProjectView {
var result; var result;
if (platform.inspect) { if (platform.inspect) {
result = platform.inspect(ident); result = platform.inspect(ident);
} else if (!platform.isRunning() && platform.readAddress) { // only inspect when stopped
// TODO: platform should know its symbols
var addr = symbolmap[ident];
if (addr) {
var size=4;
result = "$" + hex(addr,4) + ":";
for (var i=0; i<size; i++) {
var byte = platform.readAddress(addr+i);
result += " $" + hex(byte,2) + " (" + byte + ")";
if (addr2symbol[addr+1]) break; // stop if we hit another symbol
else if (i==size-1) result += " ...";
}
}
} }
if (this.inspectWidget) { if (this.inspectWidget) {
this.inspectWidget.clear(); this.inspectWidget.clear();
@ -483,7 +470,7 @@ export class DisassemblerView implements ProjectView {
return substr; return substr;
}); });
} }
var dline = hex(parseInt(a)) + "\t" + bytes + "\t" + dstr + "\n"; var dline = hex(parseInt(a), 4) + "\t" + bytes + "\t" + dstr + "\n";
s += dline; s += dline;
if (a == pc) selline = curline; if (a == pc) selline = curline;
curline++; curline++;

View File

@ -151,6 +151,13 @@ var PLATFORM_PARAMS = {
}, },
'verilog': { 'verilog': {
}, },
'astrocade': {
code_start: 0x2000,
rom_size: 0x2000,
data_start: 0x4e00,
data_size: 0x200,
stack_end: 0x5000,
},
}; };
// shim out window and document objects for security // shim out window and document objects for security
@ -995,6 +1002,7 @@ function linkSDLDZ80(step)
var asmlines = parseListing(rstout, /^\s*([0-9A-F]+)\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]\s+(\d+) (.*)/i, 4, 1, 2); var asmlines = parseListing(rstout, /^\s*([0-9A-F]+)\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]\s+(\d+) (.*)/i, 4, 1, 2);
var srclines = parseSourceLines(rstout, /^\s+\d+ ;<stdin>:(\d+):/i, /^\s*([0-9A-F]{4})/i); var srclines = parseSourceLines(rstout, /^\s+\d+ ;<stdin>:(\d+):/i, /^\s*([0-9A-F]{4})/i);
putWorkFile(fn, rstout); putWorkFile(fn, rstout);
// TODO: you have to get rid of all source lines to get asm listing
listings[fn] = { listings[fn] = {
asmlines:srclines.length ? asmlines : null, asmlines:srclines.length ? asmlines : null,
lines:srclines.length ? srclines : asmlines, lines:srclines.length ? srclines : asmlines,

View File

@ -1,13 +1,8 @@
var assert = require('assert'); var assert = require('assert');
var vm = require('vm');
var fs = require('fs'); var fs = require('fs');
var includeInThisContext = function(path) {
var code = fs.readFileSync(path);
vm.runInThisContext(code, path);
};
includeInThisContext("gen/cpu/disasm6502.js"); var disassemble6502 = require("gen/cpu/disasm6502.js").disassemble6502;
describe('6502 disassembler', function() { describe('6502 disassembler', function() {
it('Should work', function() { it('Should work', function() {

View File

@ -0,0 +1,18 @@
var assert = require('assert');
var fs = require('fs');
var disassembleZ80 = require("gen/cpu/disasmz80.js").disassembleZ80;
describe('Z80 disassembler', function() {
it('Should work', function() {
assert.deepEqual({line:"LD SP,$E800",nbytes:3}, disassembleZ80(0, 0x31, 0x00, 0xe8, 0));
assert.deepEqual({line:"DI",nbytes:1}, disassembleZ80(0, 0xF3, 0, 0, 0));
assert.deepEqual({line:"JP $0007",nbytes:3}, disassembleZ80(0, 0xC3, 0x07, 0x00, 0));
assert.deepEqual({line:"LD A,$01",nbytes:2}, disassembleZ80(0, 0x3E, 0x01, 0, 0));
assert.deepEqual({line:"LDIR",nbytes:2}, disassembleZ80(0, 0xED, 0xB0, 0, 0));
assert.deepEqual({line:"JR C,$0027",nbytes:2}, disassembleZ80(0x4e, 0x38, 0xD7, 0, 0));
assert.deepEqual({line:"XOR A",nbytes:2}, disassembleZ80(0, 0xaf, 0xd3, 0, 0));
assert.deepEqual({line:"LD IX,$41E0",nbytes:4}, disassembleZ80(0, 0xdd, 0x21, 0xe0, 0x41));
});
});