diff --git a/demo.html b/demo.html
index aaef0107..21257951 100644
--- a/demo.html
+++ b/demo.html
@@ -156,7 +156,7 @@ We're always adding new things here, so subscribe to our mailing list for update
@@ -167,7 +167,7 @@ We're always adding new things here, so subscribe to our mailing list for update
diff --git a/doc/notes.txt b/doc/notes.txt
index 38903948..b63f2152 100644
--- a/doc/notes.txt
+++ b/doc/notes.txt
@@ -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?
diff --git a/images/book_a2600_med.jpg b/images/book_a2600_med.jpg
new file mode 100644
index 00000000..bf7ad3a6
Binary files /dev/null and b/images/book_a2600_med.jpg differ
diff --git a/images/book_arcade_med.jpg b/images/book_arcade_med.jpg
new file mode 100644
index 00000000..fdb6efee
Binary files /dev/null and b/images/book_arcade_med.jpg differ
diff --git a/index.html b/index.html
index 76b0afde..2c5904ba 100644
--- a/index.html
+++ b/index.html
@@ -309,6 +309,7 @@ function require(modname) {
+
diff --git a/presets/vcs/6digit.inc b/presets/vcs/6digit.inc
index ea2938a2..b2f8cf9b 100644
--- a/presets/vcs/6digit.inc
+++ b/presets/vcs/6digit.inc
@@ -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
diff --git a/presets/vcs/examples/testlibrary.a b/presets/vcs/examples/testlibrary.a
index 07f42566..c6125acf 100644
--- a/presets/vcs/examples/testlibrary.a
+++ b/presets/vcs/examples/testlibrary.a
@@ -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
diff --git a/presets/vcs/multisprite.inc b/presets/vcs/multisprite.inc
index 585466d2..a325e242 100644
--- a/presets/vcs/multisprite.inc
+++ b/presets/vcs/multisprite.inc
@@ -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
diff --git a/presets/verilog/switches.v b/presets/verilog/switches.v
new file mode 100644
index 00000000..26a50159
--- /dev/null
+++ b/presets/verilog/switches.v
@@ -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
diff --git a/src/baseplatform.ts b/src/baseplatform.ts
index 3b0db7fe..7367ff6e 100644
--- a/src/baseplatform.ts
+++ b/src/baseplatform.ts
@@ -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) {
diff --git a/src/cpu/disasm6502.ts b/src/cpu/disasm6502.ts
index 92660a7a..7a7cfb60 100644
--- a/src/cpu/disasm6502.ts
+++ b/src/cpu/disasm6502.ts
@@ -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};
};
diff --git a/src/cpu/disasmz80.ts b/src/cpu/disasmz80.ts
new file mode 100644
index 00000000..8aaf7bb1
--- /dev/null
+++ b/src/cpu/disasmz80.ts
@@ -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};
+};
diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts
index 256e5a19..0c5844e4 100644
--- a/src/platform/vcs.ts
+++ b/src/platform/vcs.ts
@@ -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:(\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,
diff --git a/test/cli/6502/test6502disasm.js b/test/cli/6502/test6502disasm.js
index cb319d8c..737ce3ea 100644
--- a/test/cli/6502/test6502disasm.js
+++ b/test/cli/6502/test6502disasm.js
@@ -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() {
diff --git a/test/cli/z80/testz80disasm.js b/test/cli/z80/testz80disasm.js
new file mode 100644
index 00000000..cba9046e
--- /dev/null
+++ b/test/cli/z80/testz80disasm.js
@@ -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));
+ });
+});