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="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');">
<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" />
</div>
<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 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');">
<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" />
</div>
<div class="col-md-4">

View File

@ -56,8 +56,9 @@ TODO:
- VCS skips step on lsr/lsr after run to line
- update window list after building 2nd time?
- 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?

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/audio.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/project.js"></script>
<script src="gen/windows.js"></script>

View File

@ -17,35 +17,6 @@ THREE_COPIES equ %011 ; for NUSIZ registers
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.
; A = 1st BCD digit
; X = 2nd BCD digit
@ -97,13 +68,40 @@ GetDigitPtrs: subroutine
; Display the resulting 48x8 bitmap
; using the Digit0-5 pointers.
; A = color of scoreboard
DrawDigits: subroutine
.temp equ Temp1
.count equ Temp2
sta WSYNC
SLEEP 40 ; start near end of scanline
lda #7
sta .count
; clear player bitmaps first
jsr ClearGRP
; set colors
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
ldy .count ; counts backwards
lda (Digit0),y ; load B0 (1st sprite byte)
@ -119,18 +117,19 @@ DrawDigits: subroutine
tax ; -> X
lda (Digit3),y ; load B3 -> A
ldy .temp ; load B5 -> Y
.storegrp
sta GRP1 ; B3 -> [GRP1]; B2 -> GRP0
stx GRP0 ; B4 -> [GRP0]; B3 -> GRP1
sty GRP1 ; B5 -> [GRP1]; B4 -> GRP0
sta GRP0 ; ?? -> [GRP0]; B5 -> GRP1
dec .count ; go to next line
bpl .bigloop ; repeat until < 0
lda #0 ; clear the sprite registers
sta GRP0
sta GRP1
sta GRP0
sta GRP1
ClearGRP
ldx #0 ; empty out player bitmaps
stx GRP0
stx GRP1
stx GRP0
stx GRP1
rts
; Font table for digits 0-9 (8x8 pixels)
@ -141,4 +140,4 @@ Digit6Font:
hex 0006067f661e0e06003c6606067c607e
hex 003c66667c60663c00181818180c667e
hex 003c66663c66663c003c66063e66663c
hex 00 ; so top of 9 is clean

View File

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

View File

@ -45,16 +45,20 @@
; MSpriteDraw1 - Single-line sprites, no playfield.
; This kernel requires TIMER_TABLE_SETUP at the 0th
; 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.
; The playfield is updated and the sprites are positioned
; in 8-scanline segments.
; This kernel uses the timer internally, but does not
; 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,
; and MSpritePre between every frame.
; and MSpriteFrame between every frame.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -119,7 +123,7 @@ MSpriteInit subroutine
rts
; call between frames
MSpritePre subroutine
MSpriteFrame subroutine
; Do one iteration of bubble sort on sprite indices
ldx #NSprites-2
.SortLoop
@ -127,20 +131,18 @@ MSpritePre subroutine
dex
bpl .SortLoop ; loop until <= 0
; Reset scanline counter and sprite objects
ldx #0
jsr ResetCounters
stx CurIndex
stx SIndx0
stx SIndx1
stx SSize0
stx SSize1
rts
; single-line kernel routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SINGLE-LINE KERNEL
MSpriteDraw1 subroutine
.NextFindSprite
; Try to schedule sprites to both players
jsr FindAnotherSprite
jsr FindAnotherSprite
; Apply fine offsets
sta WSYNC ; start next scanline
sta HMOVE ; apply the previous fine position(s)
@ -157,6 +159,106 @@ MSpriteDraw1 subroutine
lda #191
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
DrawSprites1 subroutine
; Wait for next precise scanline
@ -166,6 +268,7 @@ DrawSprites1 subroutine
lda Timer2Scanline,y ; lookup scanline #
beq .AnotherScanline ; not if zero!
sta Scanline ; save it
DrawSprites1a
; Calculate # of lines to draw for each sprite
; Sprite Y - current scanline + sprite height
lda SIndx0
@ -193,6 +296,7 @@ DrawSprites1 subroutine
tax ; X = # of lines left to draw
beq .NoSprites ; X = 0? we're done
sta WSYNC ; next scanline
DrawSprites1b
.DrawNextScanline
; Make sure player 0 index is within bounds
ldy SIndx0
@ -227,10 +331,7 @@ DrawSprites1 subroutine
dex
bne .DrawNextScanline
; Free up both player objects by zeroing them out
stx SIndx0
stx SIndx1
stx SSize0
stx SSize1
jsr ResetCounters
sta WSYNC
stx GRP0
stx GRP1
@ -245,86 +346,41 @@ DrawSprites1 subroutine
sta COLUP0
beq .DrawSprite1 ; always taken due to lda #0
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
.KernelLoop
dec PFCount
bmi .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
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
; 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 (PF1Ptr),y ; load PF1 -> tmp
sta tmpPF1
lda (PF2Ptr),y ; load PF2 -> Y
lda (PData1),y ; bitmap for first line
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?
bpl .KernelLoop
jmp .NoMoreLines
; WSYNC and store sprite values
lda Colp0
; still have about 30 cycles left...
sta WSYNC
sty GRP1 ; GRP0 is also updated due to VDELP0 flag
stx COLUP1
sta COLUP0
; Return to caller
rts
CalcSpriteEnd subroutine
; Calculate # of lines to draw for each sprite
@ -355,16 +411,24 @@ CalcSpriteEnd subroutine
; Compute the number of 8x lines in this section
eor #$ff
clc
adc #5
adc #1
lsr
lsr
sta PFCount
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.
; Put it earlier in the sort order and try next frame.
; 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
; too close to the left edge of the screen. So we'd prefer to
; put those sprites in the player 0 slot.
; TODO: disable for 2-line sprite kernel
cmp #34 ; X < 34
bcc .Plyr1NotReady
; First let's set its horizontal offset
@ -436,7 +501,7 @@ FindAnotherSprite2 ; alternate entry point when scanline known
ldy #0
lda (PColr1),y
sta SSize1
inc SSize1 ; +1 to size
inc SSize1 ; +1 to height
jmp .SetupDone
.Plyr1NotReady
ldx SIndx0
@ -465,47 +530,12 @@ FindAnotherSprite2 ; alternate entry point when scanline known
ldy #0
lda (PColr0),y
sta SSize0
inc SSize0 ; +1 to size
inc SSize0 ; +1 to height
.SetupDone
inc CurIndex ; go to next sprite in sort order
.NoNearSprite
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
; X register contains sort index (0 to NSprites-1)
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 { hex } from "./util";
import { CodeAnalyzer } from "./analysis";
import { disassemble6502 } from "./cpu/disasm6502";
import { disassembleZ80 } from "./cpu/disasmz80";
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) {

View File

@ -1,4 +1,6 @@
import { hex } from "../util";
var OPS_6502 = [
{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
@ -258,24 +260,7 @@ var OPS_6502 = [
{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} {
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;
}
export function disassemble6502(pc:number, b0:number, b1:number, b2:number) : {line:string, nbytes:number} {
var op = OPS_6502[b0];
var s = op.mn;
@ -283,10 +268,10 @@ function disassemble6502(pc:number, b0:number, b1:number, b2:number) : {line:str
if (am == 'branch') {
var offset = (b1 < 0x80) ? (pc+2+b1) : (pc+2-(256-b1));
offset &= 0xffff;
am = '$'+formatHex(offset, 4);
am = '$'+hex(offset, 4);
} else {
am = am.replace('aa','$'+formatHex(b1, 2));
am = am.replace('AAAA','$'+formatHex(b1+(b2<<8), 4));
am = am.replace('aa','$'+hex(b1, 2));
am = am.replace('AAAA','$'+hex(b1+(b2<<8), 4));
}
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 { hex, lpad, tobin, byte2signed } from "../util";
import { CodeAnalyzer_vcs } from "../analysis";
import { disassemble6502 } from "../cpu/disasm6502";
declare var platform : Platform; // global platform object
declare var Javatari : any;
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 = [
{id:'examples/hello', chapter:4, name:'Hello 6502 and TIA'},
{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/fracpitch', name:'Fractional Pitch'},
{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/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 {
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?

View File

@ -112,19 +112,6 @@ export class SourceEditor implements ProjectView {
var result;
if (platform.inspect) {
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) {
this.inspectWidget.clear();
@ -483,7 +470,7 @@ export class DisassemblerView implements ProjectView {
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;
if (a == pc) selline = curline;
curline++;

View File

@ -151,6 +151,13 @@ var PLATFORM_PARAMS = {
},
'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
@ -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 srclines = parseSourceLines(rstout, /^\s+\d+ ;<stdin>:(\d+):/i, /^\s*([0-9A-F]{4})/i);
putWorkFile(fn, rstout);
// TODO: you have to get rid of all source lines to get asm listing
listings[fn] = {
asmlines:srclines.length ? asmlines : null,
lines:srclines.length ? srclines : asmlines,

View File

@ -1,13 +1,8 @@
var assert = require('assert');
var vm = require('vm');
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() {
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));
});
});