From 4d5c6b9063c34e3b08ce0fdbf10487d11e6f62c0 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 11 Aug 2019 10:23:56 -0400 Subject: [PATCH] williams: added test for 6809; fiddled with atari7800 --- doc/notes.txt | 5 +- presets/atari7800/sprites.dasm | 883 +++++++++++++++-------------- src/audio.ts | 37 +- src/baseplatform.ts | 5 +- src/emu.ts | 7 +- src/platform/atari7800.ts | 356 ++++++++++-- src/platform/williams.ts | 3 +- src/worker/workermain.ts | 9 + test/cli/testplatforms.js | 10 +- test/roms/williams/vidfill.asm.rom | Bin 0 -> 53 bytes tools/bin2arr.py | 1 + 11 files changed, 830 insertions(+), 486 deletions(-) create mode 100644 test/roms/williams/vidfill.asm.rom mode change 100644 => 100755 tools/bin2arr.py diff --git a/doc/notes.txt b/doc/notes.txt index 2e0da413..0f23f693 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -171,7 +171,10 @@ TODO: - popup - convert binary to hex stmts - "suggestions" (vblank overrun, variable # scanlines, etc) -- spinner doesn't always spin on sms +- SMS + - can't step back twice? + - compiler bug in chase + WEB WORKER FORMAT diff --git a/presets/atari7800/sprites.dasm b/presets/atari7800/sprites.dasm index 6e8735ab..484620db 100644 --- a/presets/atari7800/sprites.dasm +++ b/presets/atari7800/sprites.dasm @@ -1,440 +1,443 @@ -; Atari 7800 sprite sample -; Written by Daniel Boris (dboris@home.com) -; -; Assemble with DASM -; - - processor 6502 - -; ************ Hardware Adresses *************************** - -INPTCTRL equ $01 ;Input control -AUDC0 equ $15 ;Audio Control Channel 0 -AUDC1 equ $16 ;Audio Control Channel 1 -AUDF0 equ $17 ;Audio Frequency Channel 0 -AUDF1 equ $18 ;Audio Frequency Channel 1 -AUDV0 equ $19 ;Audio Volume Channel 0 -AUDV1 equ $1A ;Audio Volume Channel 1 -INPT0 equ $08 ;Paddle Control Input 0 -INPT1 equ $09 ;Paddle Control Input 1 -INPT2 equ $0A ;Paddle Control Input 2 -INPT3 equ $0B ;Paddle Control Input 3 -INPT4 equ $0C ;Player 0 Fire Button Input -INPT5 equ $0D ;Player 1 Fire Button Input - -BACKGRND equ $20 ;Background Color -P0C1 equ $21 ;Palette 0 - Color 1 -P0C2 equ $22 ;Palette 0 - Color 2 -P0C3 equ $23 ;Palette 0 - Color 3 -WSYNC equ $20 ;Wait For Sync -P1C1 equ $21 ;Palette 1 - Color 1 -P1C2 equ $22 ;Palette 1 - Color 2 -P1C3 equ $23 ;Palette 1 - Color 3 -MSTAT equ $28 ;Maria Status -P2C1 equ $29 ;Palette 2 - Color 1 -P2C2 equ $2A ;Palette 2 - Color 2 -P2C3 equ $2B ;Palette 2 - Color 3 -DPPH equ $2C ;Display List List Pointer High -P3C1 equ $2D ;Palette 3 - Color 1 -P3C2 equ $2E ;Palette 3 - Color 2 -P3C3 equ $2F ;Palette 3 - Color 3 -DPPL equ $30 ;Display List List Pointer Low -P4C1 equ $31 ;Palette 4 - Color 1 -P4C2 equ $32 ;Palette 4 - Color 2 -P4C3 equ $33 ;Palette 4 - Color 3 -CHARBASE equ $34 ;Character Base Address -P5C1 equ $35 ;Palette 5 - Color 1 -P5C2 equ $36 ;Palette 5 - Color 2 -P5C3 equ $37 ;Palette 5 - Color 3 -OFFSET equ $38 ;Unused - Store zero here -P6C1 equ $39 ;Palette 6 - Color 1 -P6C2 equ $3A ;Palette 6 - Color 2 -P6C3 equ $3B ;Palette 6 - Color 3 -CTRL equ $3C ;Maria Control Register -P7C1 equ $3D ;Palette 7 - Color 1 -P7C2 equ $3E ;Palette 7 - Color 2 -P7C3 equ $3F ;Palette 7 - Color 3 - -SWCHA equ $280 ;P0, P1 Joystick Directional Input -SWCHB equ $282 ;Console Switches -CTLSWA equ $281 ;I/O Control for SCHWA -CTLSWB equ $283 ;I/O Control for SCHWB - - SEG.U data - - -;******* Vairables ******************************** - - org $40 - -xpos ds.b 1 ;X Position of sprite -ypos ds.b 1 ;Y Position of sprite -temp ds.b 1 -dlpnt ds.w 1 -dlend ds.b 12 ;Index of end of each DL - - - - -;********************************************************** - - SEG code - - org $4000 ;Start of code - -START - sei ;Disable interrupts - cld ;Clear decimal mode - - -;******** Atari recommended startup procedure - - lda #$07 - sta INPTCTRL ;Lock into 7800 mode - lda #$7F - sta CTRL ;Disable DMA - lda #$00 - sta OFFSET - sta INPTCTRL - ldx #$FF ;Reset stack pointer - txs - -;************** Clear zero page and hardware ****** - - ldx #$40 - lda #$00 -crloop1 - sta $00,x ;Clear zero page - sta $100,x ;Clear page 1 - inx - bne crloop1 - -;************* Clear RAM ************************** - - ldy #$00 ;Clear Ram - lda #$18 ;Start at $1800 - sta $81 - lda #$00 - sta $80 -crloop3 - lda #$00 - sta ($80),y ;Store data - iny ;Next byte - bne crloop3 ;Branch if not done page - inc $81 ;Next page - lda $81 - cmp #$20 ;End at $1FFF - bne crloop3 ;Branch if not - - ldy #$00 ;Clear Ram - lda #$22 ;Start at $2200 - sta $81 - lda #$00 - sta $80 -crloop4 - lda #$00 - sta ($80),y ;Store data - iny ;Next byte - bne crloop4 ;Branch if not done page - inc $81 ;Next page - lda $81 - cmp #$27 ;End at $27FF - bne crloop4 ;Branch if not - - ldx #$00 - lda #$00 -crloop5 ;Clear 2100-213F - sta $2100,x - inx - cpx #$40 - bne crloop5 - -;************* Build DLL ******************* - -; 20 blank lines - - ldx #$00 - lda #$4F ;16 lines - sta $1800,x - inx - lda #$21 ;$2100 = blank DL - sta $1800,x - inx - lda #$00 - sta $1800,x - inx - lda #$44 ;4 lines - sta $1800,x - inx - lda #$21 - sta $1800,x - inx - lda #$00 - sta $1800,x - inx - -; 192 mode lines divided into 12 regions - - ldy #$00 -DLLloop2 - lda #$4F ;16 lines - sta $1800,x - inx - lda DLPOINTH,y - sta $1800,x - inx - lda DLPOINTL,y - sta $1800,x - inx - iny - cpy #$0D ;12 DLL entries - bne DLLloop2 - - -; 26 blank lines - - lda #$4F ;16 lines - sta $1800,x - inx - lda #$21 ;$2100 = blank DL - sta $1800,x - inx - lda #$00 - sta $1800,x - inx - lda #$4A ;10 lines - sta $1800,x - inx - lda #$21 - sta $1800,x - inx - lda #$00 - sta $1800,x - - -;***************** Setup Maria Registers **************** - - lda #$18 ;DLL at $1800 - sta DPPH - lda #$00 - sta DPPL - lda #$18 ;Setup Palette 0 - sta P0C1 - lda #$38 - sta P0C2 - lda #$58 - sta P0C3 - lda #$43 ;Enable DMA - sta CTRL - lda #$00 ;Setup ports to read mode - sta CTLSWA - sta CTLSWB - - lda #$40 ;Set initial X position of sprite - sta xpos - -mainloop - lda MSTAT ;Wait for VBLANK - and #$80 - beq mainloop - - lda SWCHA ;Read stick - and #$80 ;Pushed Right? - bne skip1 - ldx xpos ;Move sprite to right - inx - stx xpos -skip1 - lda SWCHA ;Read stick - and #$40 ;Pushed Left? - bne skip2 - ldx xpos ;Move sprite to left - dex - stx xpos -skip2 - lda SWCHA ;Read stick - and #$20 ;Pushed Down? - bne skip3 - ldx ypos ;Move sprite down - cpx #176 - beq skip3 ;Don't move if we are at the bottom - inx - stx ypos -skip3 - lda SWCHA ;Read stick - and #$10 ;Pushed Up? - bne skip4 - ldx ypos ;Move sprite up - beq skip4 ;Don't move if we are at the top - dex - stx ypos -skip4 - -;********************** reset DL ends ****************** - - ldx #$0C - lda #$00 -dlclearloop - dex - sta dlend,x - bne dlclearloop - - -;******************** build DL entries ********************* - - lda ypos ;Get Y position - and #$F0 - lsr ;Divide by 16 - lsr - lsr - lsr - tax - lda DLPOINTL,x ;Get pointer to DL that this sprite starts in - sta dlpnt - lda DLPOINTH,x - sta dlpnt+1 - - ;Create DL entry for upper part of sprite - - ldy dlend,x ;Get the index to the end of this DL - lda #$00 - sta (dlpnt),y ;Low byte of data address - iny - lda #$40 ;Mode 320x1 - sta (dlpnt),y - iny - lda ypos - and #$0F - ora #$a0 - sta (dlpnt),y - iny - lda #$1F ;Palette 0, 1 byte wide - sta (dlpnt),y - iny - lda xpos ;Horizontal position - sta (dlpnt),y - sty dlend,x - - lda ypos - and #$0F ;See if sprite is entirely within this region - beq doneDL ;branch if it is - - ;Create DL entry for lower part of sprite - - inx ;Next region - lda DLPOINTL,x ;Get pointer to next DL - sta dlpnt - lda DLPOINTH,x - sta dlpnt+1 - ldy dlend,x ;Get the index to the end of this DL - lda #$00 - sta (dlpnt),y - iny - lda #$40 ;Mode 320x1 - sta (dlpnt),y - iny - lda ypos - and #$0F - eor #$0F - sta temp - lda #$a0 - clc - sbc temp - sta (dlpnt),y - iny - lda #$1F ;Palette 0, 1 byte wide - sta (dlpnt),y - iny - lda xpos ;Horizontal position - sta (dlpnt),y - sty dlend,x -doneDL - -;************** add DL end entry on each DL ***************************** - - ldx #$0C -dlendloop - dex - lda DLPOINTL,x - sta dlpnt - lda DLPOINTH,x - sta dlpnt+1 - ldy dlend,x - iny - lda #$00 - sta (dlpnt),y - txa - bne dlendloop - -vbloop - lda MSTAT ;Wait for VBLANK to end - and #$80 - bne vbloop - - jmp mainloop ;Loop - -redraw - - -NMI - RTI - -IRQ - RTI - - -;Pointers to the DLs - -DLPOINTH - .byte $22,$22,$22,$22,$23,$23,$23,$23,$24,$24,$24,$24 -DLPOINTL - .byte $00,$40,$80,$C0,$00,$40,$80,$C0,$00,$40,$80,$C0 - - - - -;************** Graphic Data ***************************** - org $a000 - .byte %00111100 - org $a100 - .byte %00111100 - org $a200 - .byte %01000010 - org $a300 - .byte %01000010 - org $a400 - .byte %10011001 - org $a500 - .byte %10011001 - org $a600 - .byte %10100101 - org $a700 - .byte %10100101 - org $a800 - .byte %10000001 - org $a900 - .byte %10000001 - org $aA00 - .byte %10100101 - org $aB00 - .byte %10100101 - org $aC00 - .byte %01000010 - org $aD00 - .byte %01000010 - org $aE00 - .byte %00111100 - org $aF00 - .byte %00111100 - - -;************** Cart reset vector ************************** - - org $fff8 - .byte $FF ;Region verification - .byte $87 ;ROM start $4000 - .word #NMI - .word #START - .word #IRQ - - +; Atari 7800 sprite sample +; Written by Daniel Boris (dboris@home.com) +; +; Assemble with DASM +; + + processor 6502 + +; ************ Hardware Adresses *************************** + +INPTCTRL equ $01 ;Input control +AUDC0 equ $15 ;Audio Control Channel 0 +AUDC1 equ $16 ;Audio Control Channel 1 +AUDF0 equ $17 ;Audio Frequency Channel 0 +AUDF1 equ $18 ;Audio Frequency Channel 1 +AUDV0 equ $19 ;Audio Volume Channel 0 +AUDV1 equ $1A ;Audio Volume Channel 1 +INPT0 equ $08 ;Paddle Control Input 0 +INPT1 equ $09 ;Paddle Control Input 1 +INPT2 equ $0A ;Paddle Control Input 2 +INPT3 equ $0B ;Paddle Control Input 3 +INPT4 equ $0C ;Player 0 Fire Button Input +INPT5 equ $0D ;Player 1 Fire Button Input + +BACKGRND equ $20 ;Background Color +P0C1 equ $21 ;Palette 0 - Color 1 +P0C2 equ $22 ;Palette 0 - Color 2 +P0C3 equ $23 ;Palette 0 - Color 3 +WSYNC equ $20 ;Wait For Sync +P1C1 equ $21 ;Palette 1 - Color 1 +P1C2 equ $22 ;Palette 1 - Color 2 +P1C3 equ $23 ;Palette 1 - Color 3 +MSTAT equ $28 ;Maria Status +P2C1 equ $29 ;Palette 2 - Color 1 +P2C2 equ $2A ;Palette 2 - Color 2 +P2C3 equ $2B ;Palette 2 - Color 3 +DPPH equ $2C ;Display List List Pointer High +P3C1 equ $2D ;Palette 3 - Color 1 +P3C2 equ $2E ;Palette 3 - Color 2 +P3C3 equ $2F ;Palette 3 - Color 3 +DPPL equ $30 ;Display List List Pointer Low +P4C1 equ $31 ;Palette 4 - Color 1 +P4C2 equ $32 ;Palette 4 - Color 2 +P4C3 equ $33 ;Palette 4 - Color 3 +CHARBASE equ $34 ;Character Base Address +P5C1 equ $35 ;Palette 5 - Color 1 +P5C2 equ $36 ;Palette 5 - Color 2 +P5C3 equ $37 ;Palette 5 - Color 3 +OFFSET equ $38 ;Unused - Store zero here +P6C1 equ $39 ;Palette 6 - Color 1 +P6C2 equ $3A ;Palette 6 - Color 2 +P6C3 equ $3B ;Palette 6 - Color 3 +CTRL equ $3C ;Maria Control Register +P7C1 equ $3D ;Palette 7 - Color 1 +P7C2 equ $3E ;Palette 7 - Color 2 +P7C3 equ $3F ;Palette 7 - Color 3 + +SWCHA equ $280 ;P0, P1 Joystick Directional Input +SWCHB equ $282 ;Console Switches +CTLSWA equ $281 ;I/O Control for SCHWA +CTLSWB equ $283 ;I/O Control for SCHWB + + SEG.U data + + +;******* Vairables ******************************** + + org $40 + +xpos ds.b 1 ;X Position of sprite +ypos ds.b 1 ;Y Position of sprite +temp ds.b 1 +dlpnt ds.w 1 +dlend ds.b 12 ;Index of end of each DL + + + + +;********************************************************** + + SEG code + + org $4000 ;Start of code + +START + sei ;Disable interrupts + cld ;Clear decimal mode + + +;******** Atari recommended startup procedure + + lda #$07 + sta INPTCTRL ;Lock into 7800 mode + lda #$7F + sta CTRL ;Disable DMA + lda #$00 + sta OFFSET + sta INPTCTRL + ldx #$FF ;Reset stack pointer + txs + +;************** Clear zero page and hardware ****** + + ldx #$40 + lda #$00 +crloop1 + sta $00,x ;Clear zero page + sta $100,x ;Clear page 1 + inx + bne crloop1 + +;************* Clear RAM ************************** + + ldy #$00 ;Clear Ram + lda #$18 ;Start at $1800 + sta $81 + lda #$00 + sta $80 +crloop3 + lda #$00 + sta ($80),y ;Store data + iny ;Next byte + bne crloop3 ;Branch if not done page + inc $81 ;Next page + lda $81 + cmp #$20 ;End at $1FFF + bne crloop3 ;Branch if not + + ldy #$00 ;Clear Ram + lda #$22 ;Start at $2200 + sta $81 + lda #$00 + sta $80 +crloop4 + lda #$00 + sta ($80),y ;Store data + iny ;Next byte + bne crloop4 ;Branch if not done page + inc $81 ;Next page + lda $81 + cmp #$27 ;End at $27FF + bne crloop4 ;Branch if not + + ldx #$00 + lda #$00 +crloop5 ;Clear 2100-213F + sta $2100,x + inx + cpx #$40 + bne crloop5 + +;************* Build DLL ******************* + +; 20 blank lines + + ldx #$00 + lda #$4F ;16 lines + sta $1800,x + inx + lda #$21 ;$2100 = blank DL + sta $1800,x + inx + lda #$00 + sta $1800,x + inx + lda #$44 ;4 lines + sta $1800,x + inx + lda #$21 + sta $1800,x + inx + lda #$00 + sta $1800,x + inx + +; 192 mode lines divided into 12 regions + + ldy #$00 +DLLloop2 + lda #$4F ;16 lines + sta $1800,x + inx + lda DLPOINTH,y + sta $1800,x + inx + lda DLPOINTL,y + sta $1800,x + inx + iny + cpy #$0D ;12 DLL entries + bne DLLloop2 + + +; 26 blank lines + + lda #$4F ;16 lines + sta $1800,x + inx + lda #$21 ;$2100 = blank DL + sta $1800,x + inx + lda #$00 + sta $1800,x + inx + lda #$4A ;10 lines + sta $1800,x + inx + lda #$21 + sta $1800,x + inx + lda #$00 + sta $1800,x + + +;***************** Setup Maria Registers **************** + + lda #$80 + sta BACKGRND ;background color + lda #$18 ;DLL at $1800 + sta DPPH + lda #$00 + sta DPPL + lda #$18 ;Setup Palette 0 + sta P0C1 + lda #$38 + sta P0C2 + lda #$58 + sta P0C3 + lda #$43 ;Enable DMA + sta CTRL + lda #$00 ;Setup ports to read mode + sta CTLSWA + sta CTLSWB + + lda #$40 ;Set initial X position of sprite + sta xpos + +mainloop + lda MSTAT ;Wait for VBLANK + and #$80 + beq mainloop + + lda SWCHA ;Read stick + and #$80 ;Pushed Right? + bne skip1 + ldx xpos ;Move sprite to right + inx + stx xpos +skip1 + lda SWCHA ;Read stick + and #$40 ;Pushed Left? + bne skip2 + ldx xpos ;Move sprite to left + dex + stx xpos +skip2 + lda SWCHA ;Read stick + and #$20 ;Pushed Down? + bne skip3 + ldx ypos ;Move sprite down + cpx #176 + beq skip3 ;Don't move if we are at the bottom + inx + stx ypos +skip3 + lda SWCHA ;Read stick + and #$10 ;Pushed Up? + bne skip4 + ldx ypos ;Move sprite up + beq skip4 ;Don't move if we are at the top + dex + stx ypos +skip4 + +;********************** reset DL ends ****************** + + ldx #$0C + lda #$00 +dlclearloop + dex + sta dlend,x + bne dlclearloop + + +;******************** build DL entries ********************* + + lda ypos ;Get Y position + and #$F0 + lsr ;Divide by 16 + lsr + lsr + lsr + tax + lda DLPOINTL,x ;Get pointer to DL that this sprite starts in + sta dlpnt + lda DLPOINTH,x + sta dlpnt+1 + + ;Create DL entry for upper part of sprite + + ldy dlend,x ;Get the index to the end of this DL + lda #$00 + sta (dlpnt),y ;Low byte of data address + iny + lda #$40 ;Mode 320x1 + sta (dlpnt),y + iny + lda ypos + and #$0F + ora #$a0 + sta (dlpnt),y + iny + lda #$1F ;Palette 0, 1 byte wide + sta (dlpnt),y + iny + lda xpos ;Horizontal position + sta (dlpnt),y + sty dlend,x + + lda ypos + and #$0F ;See if sprite is entirely within this region + beq doneDL ;branch if it is + + ;Create DL entry for lower part of sprite + + inx ;Next region + lda DLPOINTL,x ;Get pointer to next DL + sta dlpnt + lda DLPOINTH,x + sta dlpnt+1 + ldy dlend,x ;Get the index to the end of this DL + lda #$00 + sta (dlpnt),y + iny + lda #$40 ;Mode 320x1 + sta (dlpnt),y + iny + lda ypos + and #$0F + eor #$0F + sta temp + lda #$a0 + clc + sbc temp + sta (dlpnt),y + iny + lda #$1F ;Palette 0, 1 byte wide + sta (dlpnt),y + iny + lda xpos ;Horizontal position + sta (dlpnt),y + sty dlend,x +doneDL + +;************** add DL end entry on each DL ***************************** + + ldx #$0C +dlendloop + dex + lda DLPOINTL,x + sta dlpnt + lda DLPOINTH,x + sta dlpnt+1 + ldy dlend,x + iny + lda #$00 + sta (dlpnt),y + txa + bne dlendloop + +vbloop + lda MSTAT ;Wait for VBLANK to end + and #$80 + bne vbloop + + jmp mainloop ;Loop + +redraw + + +NMI + RTI + +IRQ + RTI + + +;Pointers to the DLs + +DLPOINTH + .byte $22,$22,$22,$22,$23,$23,$23,$23,$24,$24,$24,$24 +DLPOINTL + .byte $00,$40,$80,$C0,$00,$40,$80,$C0,$00,$40,$80,$C0 + + + + +;************** Graphic Data ***************************** +;set org and fill character + org $a000,0 + .byte %00111100 + org $a100 + .byte %00111100 + org $a200 + .byte %01000010 + org $a300 + .byte %01000010 + org $a400 + .byte %10011001 + org $a500 + .byte %10011001 + org $a600 + .byte %10100101 + org $a700 + .byte %10100101 + org $a800 + .byte %10000001 + org $a900 + .byte %10000001 + org $aA00 + .byte %10100101 + org $aB00 + .byte %10100101 + org $aC00 + .byte %01000010 + org $aD00 + .byte %01000010 + org $aE00 + .byte %00111100 + org $aF00 + .byte %00111100 + + +;************** Cart reset vector ************************** + + org $fff8 + .byte $FF ;Region verification + .byte $87 ;ROM start $4000 + .word #NMI + .word #START + .word #IRQ + + diff --git a/src/audio.ts b/src/audio.ts index 8a77fdd3..15d0c486 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -122,9 +122,18 @@ export var POKEYDeviceChannel = function() { var bit1 = new Uint8Array( [ 0,1 ] ); var bit4 = new Uint8Array( [ 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 ] ); var bit5 = new Uint8Array( [ 0,0,1,1,0,0,0,1,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,0,0,0,0,0,1 ] ); + var bit9 = new Uint8Array( [ 0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,0,1,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,1,1,0,1,1,0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,1,0,0,0,1,1,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,1,1,0,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,1,1,0,0,1,1,1,1,0,0,1,0,0,0,1,1,1,0,0,1,1,0,1,0,1,1,0,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,1,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,0,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,1,1,0,0,1,0,1,0,1,1,1,0,0,0,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,1,1,0,0,1,0,0,1,0,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,0,1,0,0,1,0,1,0,1,0,1,1,1,0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0 ] ); + var div31 = new Uint8Array( [ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 ] ); var bit17 = new Uint8Array(1<<17); var bit17_5 = new Uint8Array(1<<17); - var bit5_4 = new Uint8Array(1<<17); + var bit5_4 = new Uint8Array(1<<9); + // TODO + var bit6 = bit9; + var bit15_4 = bit5_4; + var bit5_2 = bit5_4; + var bit5_6 = bit5_4; + var bit31 = bit9; + var bit93 = bit31; for (var i=0; i 0.5 ? 1 : 0; bit17_5[i] = bit17[i] & bit5[i % bit5.length]; @@ -134,6 +143,12 @@ export var POKEYDeviceChannel = function() { bit17_5, bit5, bit5_4, bit5, bit17, bit1, bit4, bit1 ]; + var tiawavetones = [ + bit1, bit4, bit15_4, bit5_4, + bit1, bit1, bit31, bit5_2, + bit9, bit5, bit31, bit1, + bit6, bit6, bit93, bit5_6 + ]; // registers var regs = new Uint8Array(16); @@ -212,6 +227,24 @@ export var POKEYDeviceChannel = function() { } } } + + this.setTIARegister = function(addr, value) { + switch (addr) { + case 0x17: + case 0x18: + regs[(addr&1)*4] = value & 0x1f; + dirty = true; + break; + case 0x15: + case 0x16: + waveforms[(addr&1)*2] = tiawavetones[value & 0xf]; + break; + case 0x19: + case 0x1a: + volume[(addr&1)*2] = value & 0xf; + break; + } + } this.generate = function (length) { if (dirty) { @@ -236,7 +269,7 @@ export var POKEYDeviceChannel = function() { } } } - sample *= 128; + sample *= 64; buffer[s] = sample; buffer[s+1] = sample; } diff --git a/src/baseplatform.ts b/src/baseplatform.ts index f3652bb4..6d1fd4f7 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -514,7 +514,7 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform { return this._cpu; } - getProbe() { return this.probe; } + getProbe() { return this.probe; } // TODO? getPC() { return this._cpu.getPC(); } getSP() { return this._cpu.getSP(); } @@ -691,6 +691,9 @@ export abstract class Base6809Platform extends BaseZ80Platform { return cpu; } + getPC() { return this._cpu.PC; } + getSP() { return this._cpu.SP; } + runUntilReturn() { var depth = 1; this.runEval((c:CpuState) => { diff --git a/src/emu.ts b/src/emu.ts index 28906868..ae228225 100644 --- a/src/emu.ts +++ b/src/emu.ts @@ -575,12 +575,15 @@ export class ControllerPoller { } -export function padBytes(data:Uint8Array|number[], len:number) : Uint8Array { +export function padBytes(data:Uint8Array|number[], len:number, padstart?:boolean) : Uint8Array { if (data.length > len) { throw Error("Data too long, " + data.length + " > " + len); } var r = new RAM(len); - r.mem.set(data); + if (padstart) + r.mem.set(data, len-data.length); + else + r.mem.set(data); return r.mem; } diff --git a/src/platform/atari7800.ts b/src/platform/atari7800.ts index 5c1f5af0..2d781647 100644 --- a/src/platform/atari7800.ts +++ b/src/platform/atari7800.ts @@ -1,28 +1,269 @@ "use strict"; import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, getToolForFilename_6502 } from "../baseplatform"; -import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, getMousePos } from "../emu"; +import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, getMousePos, EmuHalt } from "../emu"; import { hex, lzgmini, stringToByteArray, lpad, rpad, rgb2bgr } from "../util"; import { MasterAudio, POKEYDeviceChannel, newPOKEYAudio } from "../audio"; declare var jt; // for 6502 +// https://atarihq.com/danb/a7800.shtml +// https://atarihq.com/danb/files/maria_r1.txt +// https://sites.google.com/site/atari7800wiki/ + var Atari7800_PRESETS = [ {id:'sprites.dasm', name:'Sprites (ASM)'}, ]; +const SWCHA = 0; +const SWCHB = 1; +const INPT4 = 2; +const INPT5 = 3; + const Atari7800_KEYCODE_MAP = makeKeycodeMap([ - [Keys.VK_SPACE, 0, 0], - [Keys.VK_ENTER, 0, 0], + [Keys.A, INPT4, -0x80], + //[Keys.B, 0, 1], + //[Keys.SELECT, 0, 2], + //[Keys.START, 0, 3], + [Keys.UP, SWCHA, -0x10], + [Keys.DOWN, SWCHA, -0x20], + [Keys.LEFT, SWCHA, -0x40], + [Keys.RIGHT, SWCHA, -0x80], + + [Keys.P2_A, INPT5, -0x80], + //[Keys.P2_B, 1, 1], + //[Keys.P2_SELECT, 1, 2], + //[Keys.P2_START, 1, 3], + [Keys.P2_UP, SWCHA, -0x01], + [Keys.P2_DOWN, SWCHA, -0x02], + [Keys.P2_LEFT, SWCHA, -0x04], + [Keys.P2_RIGHT, SWCHA, -0x08], ]); // http://www.ataripreservation.org/websites/freddy.offenga/megazine/ISSUE5-PALNTSC.html +// http://7800.8bitdev.org/index.php/7800_Software_Guide#APPENDIX_4:_FRAME_TIMING const CLK = 3579545; const cpuFrequency = 1789772; const linesPerFrame = 262; -const colorClocksPerLine = 228*2; +const numVisibleLines = 258-16; +const colorClocksPerLine = 454; const romLength = 0xc000; +// TIA chip + +class TIA { + regs = new Uint8Array(0x20); + + reset() { + // TODO + } + read(a : number) : number { + return this.regs[a] | 0; + } + write(a : number, v : number) { + this.regs[a] = v; + } + saveState() { + return { + regs: this.regs.slice(0) + }; + } + loadState(s) { + for (let i=0; i<32; i++) + this.write(i, s.regs[i]); + } + static stateToLongString(state) : string { + let s = ""; + s += dumpRAM(state.regs, 0, 32); + return s; + } +} + +// MARIA chip + +class MARIA { + bus; + cycles : number = 0; + regs = new Uint8Array(0x20); + offset : number = -1; + dll : number = 0; + dlstart : number = 0; + dli : boolean = false; + h16 : boolean = false; + h8 : boolean = false; + pixels = new Uint8Array(320); + + reset() { + // TODO + } + read(a : number) : number { + return this.regs[a] | 0; + } + write(a : number, v : number) { + this.regs[a] = v; + //console.log(hex(a), '=', hex(v)); + } + saveState() { + return { + regs: this.regs.slice(0), + offset: this.offset, + dll: this.dll, + dlstart: this.dlstart, + dli: this.dli, + h16: this.h16, + h8: this.h8, + }; + } + loadState(s) { + for (let i=0; i<32; i++) + this.write(i, s.regs[i]); + this.offset = s.offset; + this.dll = s.dll; + this.dlstart = s.dlstart; + this.dli = s.dli; + this.h16 = s.h16; + this.h8 = s.h8; + } + isDMAEnabled() { + return (this.regs[0x1c] & 0x60) == 0x40; + } + getDLLStart() { + return (this.regs[0x0c] << 8) + this.regs[0x10]; + } + getCharBaseAddress() { + return (this.regs[0x14] << 8) + this.offset; + } + setVBLANK(b : boolean) { + if (b) { + this.regs[0x08] |= 0x80; + this.offset = -1; + this.dll = this.getDLLStart(); // TODO? + } else { + this.regs[0x08] &= ~0x80; + } + } + readDLLEntry(bus) { + var x = bus.read(this.dll); + this.offset = (x & 0xf); + this.dli = (x & 0x80) != 0; + this.h16 = (x & 0x40) != 0; + this.h8 = (x & 0x20) != 0; + this.dlstart = (bus.read(this.dll+1)<<8) + bus.read(this.dll+2); + //console.log(hex(this.dll,4), this.offset, hex(this.dlstart,4)); + this.dll = (this.dll + 3) & 0xffff; // TODO: can only cross 1 page + this.cycles += 8; + } + isHoley(a : number) : boolean { + if (a & 0x8000) { + if (this.h16 && (a & 0x1000)) return true; + if (this.h8 && (a & 0x800)) return true; + } + return false; + } + readDMA(a : number) : number { + if (this.isHoley(a)) + return 0; + else { + this.cycles += 3; + return this.bus.read(a); + } + } + doDMA(bus) { + this.bus = bus; + this.cycles = 0; + this.pixels.fill(this.regs[0x0]); + if (this.isDMAEnabled()) { + this.cycles += 16; + // time for a new DLL entry? + if (this.offset < 0) { + this.readDLLEntry(bus); + } + // read the DL (only can span two pages) + var dlhi = this.dlstart & 0xff00; + var dlofs = this.dlstart & 0xff; + do { + // read DL entry + var b0 = bus.read(dlhi + ((dlofs+0) & 0x1ff)); + var b1 = bus.read(dlhi + ((dlofs+1) & 0x1ff)); + if (b1 == 0) break; // end of DL + var b2 = bus.read(dlhi + ((dlofs+2) & 0x1ff)); + var b3 = bus.read(dlhi + ((dlofs+3) & 0x1ff)); + var writemode = 0; + var indirect = false; + // extended header? + if ((b1 & 31) == 0) { + var pal = b3 >> 5; + var width = 32 - (b3 & 31); + var xpos = bus.read(dlhi + ((dlofs+4) & 0x1ff)); + writemode = b1 & 0x80; + indirect = (b1 & 0x20) != 0; + dlofs += 5; + } else { + // direct mode + var xpos = b3; + var pal = b1 >> 5; + var width = 32 - (b1 & 31); + dlofs += 4; + } + var gfxadr = b0 + (((b2 + (indirect?0:this.offset)) & 0xff) << 8); + xpos *= 2; + this.cycles += 4; + // copy graphics data (direct) + // TODO + var readmode = (this.regs[0x1c] & 0x3) + (writemode?4:0); + //if (this.offset == 0) console.log(hex(dla,4), hex(gfxadr,4), xpos, width, pal, readmode); + for (var i=0; i> 6) & 3; + if (col > 0) { + this.pixels[xpos] = this.pixels[xpos+1] = this.regs[(pal<<2) + col]; + } + data <<= 2; + xpos = (xpos + 2) & 0x1ff; + } + break; + case 2: // 320 B/D + case 3: // 320 A/C + for (var j=0; j<8; j++) { + var col = data & 1; + if (col > 0) { + this.pixels[xpos] = this.regs[(pal<<2) + col]; + } + data >>= 1; + xpos = (xpos + 1) & 0x1ff; + } + break; + } + } + } while (1); + // decrement offset + this.offset -= 1; + } + return this.cycles; + } + doInterrupt() : boolean { + return this.dli && this.offset == 0; + } + static stateToLongString(state) : string { + let s = ""; + s += dumpRAM(state.regs, 0, 32); + s += "\n DLL: $" + hex((state.regs[0x0c] << 8) + state.regs[0x10],4) + " @ $" + hex(state.dll,4); + s += "\n DL: $" + hex(state.dlstart,4); + s += "\nOffset: " + state.offset; + s += "\n DLI? " + state.dli; + return s; + } +} + +// Atari 7800 + class Atari7800Platform extends Base6502Platform implements Platform { mainElement : HTMLElement; @@ -34,12 +275,16 @@ class Atari7800Platform extends Base6502Platform implements Platform { video; audio; timer : AnimationTimer; - inputs = new Uint8Array(4); + inputs = new Uint8Array(8); + regs6532 = new Uint8Array(4); scanline : number = 0; + tia : TIA = new TIA(); + maria : MARIA = new MARIA(); constructor(mainElement : HTMLElement) { super(); this.mainElement = mainElement; + this.inputs.fill(0xff); } getPresets() { @@ -53,27 +298,51 @@ class Atari7800Platform extends Base6502Platform implements Platform { // TODO: TIA access wastes a cycle this.bus = { read: newAddressDecoder([ - [0x0040, 0x00ff, 0xffff, (a) => { return this.ram[a]; }], - [0x0140, 0x01ff, 0xffff, (a) => { return this.ram[a]; }], - [0x1800, 0x27ff, 0xffff, (a) => { return this.ram[a - 0x1600]; }], + [0x0008, 0x000b, 0x04, (a) => { return this.inputs[a]; }], + [0x000c, 0x000d, 0x01, (a) => { return (this.inputs[INPT4 + a] & 0x80) ? 0x80 : 0x00; }], + [0x0000, 0x001f, 0x1f, (a) => { return this.tia.read(a); }], + [0x0020, 0x003f, 0x1f, (a) => { return this.maria.read(a); }], + [0x0040, 0x00ff, 0xff, (a) => { return this.ram[a + 0x800]; }], + [0x0100, 0x013f, 0xff, (a) => { return this.bus.read(a); }], // shadow + [0x0140, 0x01ff, 0x1ff, (a) => { return this.ram[a + 0x800]; }], + [0x0280, 0x02ff, 0x3, (a) => { return this.inputs[a]; }], + [0x1800, 0x27ff, 0xffff, (a) => { return this.ram[a - 0x1800]; }], + [0x2800, 0x3fff, 0x7ff, (a) => { return this.bus.read(a | 0x2000); }], // shadow [0x4000, 0xffff, 0xffff, (a) => { return this.rom ? this.rom[a - 0x4000] : 0; }], - //[0xf000, 0xffff, 0xfff, (a) => { return this.bios ? this.bios[a] : 0; }], + [0x0000, 0xffff, 0xffff, (a) => { throw new EmuHalt("Read @ " + hex(a,4)); }] ]), write: newAddressDecoder([ - [0x0040, 0x00ff, 0xffff, (a,v) => { this.ram[a] = v; }], - [0x0140, 0x01ff, 0xffff, (a,v) => { this.ram[a] = v; }], - [0x1800, 0x27ff, 0xffff, (a,v) => { this.ram[a - 0x1600] = v; }], + [0x0015, 0x001A, 0x1f, (a,v) => { this.audio.pokey1.setTIARegister(a, v); }], + [0x0000, 0x001f, 0x1f, (a,v) => { this.tia.write(a,v); }], + [0x0020, 0x003f, 0x1f, (a,v) => { this.maria.write(a,v); }], + [0x0040, 0x00ff, 0xff, (a,v) => { this.ram[a + 0x800] = v; }], + [0x0140, 0x01ff, 0x1ff, (a,v) => { this.ram[a + 0x800] = v; }], + [0x0280, 0x02ff, 0x3, (a,v) => { this.regs6532[a] = v; /*TODO*/ }], + [0x1800, 0x27ff, 0xffff, (a,v) => { this.ram[a - 0x1800] = v; }], + [0x2800, 0x3fff, 0x7ff, (a,v) => { this.bus.write(a | 0x2000, v); }], + [0x0000, 0xffff, 0xffff, (a,v) => { throw new EmuHalt("Write @ " + hex(a,4) + " " + hex(v,2)); }] ]), }; this.cpu.connectBus(this.bus); // create video/audio - this.video = new RasterVideo(this.mainElement, 320, 192); + this.video = new RasterVideo(this.mainElement, 320, numVisibleLines); this.audio = newPOKEYAudio(1); this.video.create(); setKeyboardFromMap(this.video, this.inputs, Atari7800_KEYCODE_MAP, (o,key,code,flags) => { // TODO }); this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); + // setup mouse events + var rasterPosBreakFn = (e) => { + if (e.shiftKey) { + var clickpos = getMousePos(e.target, e); + this.runEval( (c) => { + return (this.getRasterScanline() == (clickpos.y|0)); + }); + } + }; + var jacanvas = $("#emulator").find("canvas"); + jacanvas.mousedown(rasterPosBreakFn); } advance(novideo : boolean) { @@ -81,39 +350,47 @@ class Atari7800Platform extends Base6502Platform implements Platform { var iofs = 0; var debugCond = this.getDebugCallback(); var rgb; - var freeClocks = 0; - // load controls - // TODO - //gtia.regs[0x10] = inputs[0] ^ 1; + var mariaClocks = 0; // visible lines for (var sl=0; sl 0) { - freeClocks--; - if (debugCond && debugCond()) { - debugCond = null; - i = 999; - sl = 999; - break; - } - this.cpu.clockPulse(); + this.scanline = sl; + this.maria.setVBLANK(sl >= numVisibleLines); + mariaClocks += 454; + // TODO: 7 cycles at start of line + if (this.maria.isDMAEnabled()) { + // do DMA for scanline + mariaClocks -= this.maria.doDMA(this.bus); + // copy line to frame buffer + for (var i=0; i<320; i++) { + idata[iofs++] = COLORS_RGBA[this.maria.pixels[i]]; } + // do interrupt? + if (this.maria.doInterrupt()) { + mariaClocks -= this.cpu.setNMIAndWait() * 4; + } + } + // iterate CPU with free clocks + while (mariaClocks > 0) { + mariaClocks -= 4; + if (debugCond && debugCond()) { + debugCond = null; + sl = 999; + break; + } + this.cpu.clockPulse(); } } // update video frame if (!novideo) { this.video.updateFrame(); // set background/border color - let bkcol = 0; //gtia.regs[COLBK]; + let bkcol = this.maria.regs[0x0]; $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]); } } loadROM(title, data) { - this.rom = padBytes(data, romLength); + this.rom = padBytes(data, romLength, true); this.reset(); } @@ -138,6 +415,8 @@ class Atari7800Platform extends Base6502Platform implements Platform { reset() { this.cpu.reset(); + this.tia.reset(); + this.maria.reset(); /* // execute until out of BIOS for (var i=0; i<20000; i++) { @@ -157,13 +436,16 @@ class Atari7800Platform extends Base6502Platform implements Platform { this.cpu.loadState(state.c); this.fixPC(state.c); this.ram.set(state.b); + this.tia.loadState(state.tia); + this.maria.loadState(state.maria); this.loadControlsState(state); } - saveState() { return { c:this.getCPUState(), b:this.ram.slice(0), + tia:this.tia.saveState(), + maria:this.maria.saveState(), in:this.inputs.slice(0) }; } @@ -186,18 +468,16 @@ class Atari7800Platform extends Base6502Platform implements Platform { return this.scanline; } - /* getDebugCategories() { - return super.getDebugCategories().concat(['ANTIC','GTIA']); + return super.getDebugCategories().concat(['TIA','MARIA']); } getDebugInfo(category, state) { switch (category) { - case 'ANTIC': return ANTIC.stateToLongString(state.antic); - case 'GTIA': return GTIA.stateToLongString(state.gtia); + case 'TIA': return TIA.stateToLongString(state.tia); + case 'MARIA': return MARIA.stateToLongString(state.maria) + "\nScanline: " + this.scanline; default: return super.getDebugInfo(category, state); } } - */ } /// diff --git a/src/platform/williams.ts b/src/platform/williams.ts index 7989ff41..6f44e416 100644 --- a/src/platform/williams.ts +++ b/src/platform/williams.ts @@ -23,6 +23,7 @@ var WilliamsPlatform = function(mainElement, proto) { var portsel = 0; var banksel = 0; var watchdog_counter; + var watchdog_enabled = false; var pia6821 = new RAM(8).mem; var blitregs = new RAM(8).mem; @@ -334,7 +335,7 @@ var WilliamsPlatform = function(mainElement, proto) { drawDisplayByte(i, ram.mem[i]); screenNeedsRefresh = false; } - if (watchdog_counter-- <= 0) { + if (watchdog_enabled && watchdog_counter-- <= 0) { console.log("WATCHDOG FIRED, PC =", cpu.getPC().toString(16)); // TODO: alert on video // TODO: this.breakpointHit(cpu.T()); this.reset(); diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index 6209cf86..e34c6e3d 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -274,6 +274,15 @@ var PLATFORM_PARAMS = { define: '__ATARI7800__', cfgfile: 'atari7800.cfg', libargs: ['atari7800.lib'], + extra_segments:[ + {name:'TIA',start:0x00,size:0x20,type:'io'}, + {name:'MARIA',start:0x20,size:0x20,type:'io'}, + {name:'RAM (6166 Block 0)',start:0x40,size:0xc0,type:'ram'}, + {name:'RAM (6166 Block 1)',start:0x140,size:0xc0,type:'ram'}, + {name:'PIA',start:0x280,size:0x18,type:'io'}, + {name:'RAM',start:0x1800,size:0x1000,type:'ram'}, // TODO: shadow ram + {name:'Cartridge ROM',start:0x4000,size:0xc000,type:'rom'}, + ], }, }; diff --git a/test/cli/testplatforms.js b/test/cli/testplatforms.js index 18caa63e..94bcbcee 100644 --- a/test/cli/testplatforms.js +++ b/test/cli/testplatforms.js @@ -15,6 +15,7 @@ dom.window.Audio = null; global.Image = function() { } global['$'] = require("jquery/jquery-2.2.3.min.js"); includeInThisContext('src/cpu/z80.js'); +includeInThisContext('src/cpu/6809.js'); global['buildZ80'] = global.window.buildZ80; includeInThisContext("javatari.js/release/javatari/javatari.js"); Javatari.AUTO_START = false; @@ -232,7 +233,14 @@ describe('Platform Replay', () => { }); }); - it('Should run williams', () => { + it('Should run williams 6809', () => { + var platform = testPlatform('williams', 'vidfill.asm.rom', 72, (platform, frameno) => { + if (frameno == 62) { + keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); + } + }); + }); + it('Should run williams-z80', () => { var platform = testPlatform('williams-z80', 'game1.c.rom', 72, (platform, frameno) => { if (frameno == 62) { keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); diff --git a/test/roms/williams/vidfill.asm.rom b/test/roms/williams/vidfill.asm.rom new file mode 100644 index 0000000000000000000000000000000000000000..317e63ea950329411090be924962a07c7e8d892e GIT binary patch literal 53 zcmeBUU|814pt`*C0D}Qz4}*Z($36yzHp}g&|F3TW3d~?o`^3l0$i%`Spy1HZap1v+ JAAkP+2LSBV6+Qp} literal 0 HcmV?d00001 diff --git a/tools/bin2arr.py b/tools/bin2arr.py old mode 100644 new mode 100755 index 29a14fba..7bc1b0d7 --- a/tools/bin2arr.py +++ b/tools/bin2arr.py @@ -1,3 +1,4 @@ +#!/usr/bin/python import sys