diff --git a/css/ui.css b/css/ui.css index a32f68c4..ee1bdab7 100644 --- a/css/ui.css +++ b/css/ui.css @@ -409,6 +409,10 @@ div.markdown th { color: #333; background-color:#999; } +.segment:hover { + border-color: #ffffff; + cursor: pointer; +} .segment.segment-ram { background-color:#aaeeaa; } diff --git a/presets/nes/ex1.asm b/presets/nes/ex1.asm index 9cc9bbf7..81c00fc0 100644 --- a/presets/nes/ex1.asm +++ b/presets/nes/ex1.asm @@ -54,18 +54,17 @@ HelloMsg: ; set palette colors SetPalette: subroutine - ldy #$00 - lda #$3f - sta PPU_ADDR - sty PPU_ADDR - ldx #32 + ldy #$00 ; Y = $00 (also palette index) + lda #$3f ; A = $3F + sta PPU_ADDR ; $3F?? -> PPU address + sty PPU_ADDR ; $3F00 -> PPU address .loop: - lda Palette,y - sta PPU_DATA - iny - dex - bne .loop - rts + lda Palette,y ; lookup byte in ROM + sta PPU_DATA ; store byte to PPU data + iny ; Y = Y + 1 + cpy #32 ; is Y equal to 32? + bne .loop ; not yet, loop + rts ; return to caller ;;;;; COMMON SUBROUTINES diff --git a/presets/nes/ex2.asm b/presets/nes/ex2.asm index 906f4acb..a6e321e5 100644 --- a/presets/nes/ex2.asm +++ b/presets/nes/ex2.asm @@ -38,31 +38,30 @@ Start: FillVRAM: subroutine txa ldy #$20 - sty PPU_ADDR - sta PPU_ADDR - ldy #$10 + sty PPU_ADDR ; $20?? -> PPU address + sta PPU_ADDR ; $2000 -> PPU address + ldy #$10 ; set $10 pages ($1000 bytes) .loop: - stx PPU_DATA - inx - bne .loop - dey - bne .loop - rts + stx PPU_DATA ; X -> PPU data port + inx ; X = X + 1 + bne .loop ; repeat until 256 bytes + dey ; Y = Y - 1 + bne .loop ; repeat until Y is 0 + rts ; return to caller ; set palette colors SetPalette: subroutine - ldy #$00 - lda #$3f - sta PPU_ADDR - sty PPU_ADDR - ldx #32 + ldy #$00 ; Y = $00 (also palette index) + lda #$3f ; A = $3F + sta PPU_ADDR ; $3F?? -> PPU address + sty PPU_ADDR ; $3F00 -> PPU address .loop: - lda Palette,y - sta PPU_DATA - iny - dex - bne .loop - rts + lda Palette,y ; lookup byte in ROM + sta PPU_DATA ; store byte to PPU data + iny ; Y = Y + 1 + cpy #32 ; is Y equal to 32? + bne .loop ; not yet, loop + rts ; return to caller ;;;;; COMMON SUBROUTINES diff --git a/presets/nes/horizmask.c b/presets/nes/horizmask.c index 6138d547..d5b8fb9e 100644 --- a/presets/nes/horizmask.c +++ b/presets/nes/horizmask.c @@ -21,11 +21,11 @@ void put_str(unsigned int adr, const char *str) { vram_write(str, strlen(str)); // write bytes to PPU } -static word x_scroll = 0; -static byte bldg_height = 8; -static byte bldg_width = 8; -static byte bldg_char = 1; -static byte bldg_attr = 0x55; +word x_scroll; // X scroll amount in pixels +byte bldg_height; // building height in tiles +byte bldg_width; // building width in tiles +byte bldg_char; // character to draw +byte bldg_attr; // attribute table value #define PLAYROWS 24 @@ -34,6 +34,13 @@ word nt2attraddr(word a) { ((a >> 4) & 0x38) | ((a >> 2) & 0x07); } +void new_building() { + bldg_height = (rand8() & 7) + 2; + bldg_width = (rand8() & 3) * 4 + 4; + bldg_char = (rand8() & 15); + bldg_attr = rand8(); +} + void update_nametable() { word addr; char buf[PLAYROWS]; @@ -47,6 +54,8 @@ void update_nametable() { // create vertical slice // clear empty space memset(buf, 0, PLAYROWS-bldg_height); + // draw a random star + buf[rand8() & 31] = '.'; // draw roof buf[PLAYROWS-bldg_height-1] = bldg_char & 3; // draw rest of building @@ -66,15 +75,15 @@ void update_nametable() { } // generate new building? if (--bldg_width == 0) { - bldg_height = (rand8() & 7) + 2; - bldg_width = (rand8() & 3) * 4 + 4; - bldg_char = (rand8() & 15); - bldg_attr = rand8(); + new_building(); } } // function to scroll window up and down until end void scroll_demo() { + // make 1st building + new_building(); + x_scroll = 0; // infinite loop while (1) { // update nametable every 8 pixels diff --git a/presets/nes/neslib1.c b/presets/nes/neslib1.c new file mode 100644 index 00000000..8f6e14b8 --- /dev/null +++ b/presets/nes/neslib1.c @@ -0,0 +1,34 @@ + +#include "neslib.h" +#include + +// link the pattern table into CHR ROM +//#link "chr_generic.s" + +// function to write a string into the name table +// adr = start address in name table +// str = pointer to string +void put_str(unsigned int adr, const char *str) { + vram_adr(adr); // set PPU read/write address + vram_write(str, strlen(str)); // write bytes to PPU +} + +// main function, run after console reset +void main(void) { + // set palette colors + pal_col(1,0x04); + pal_col(2,0x20); + pal_col(3,0x30); + + // write text to name table + put_str(NTADR_A(2,2),"HELLO, WORLD!"); + put_str(NTADR_A(2,4),"THIS CODE PRINTS SOME TEXT"); + put_str(NTADR_A(2,5),"USING ASCII-ENCODED CHARACTER"); + put_str(NTADR_A(2,6),"SET WITH CAPITAL LETTERS ONLY"); + + // enable PPU rendering (turn on screen) + ppu_on_all(); + + // infinite loop + while (1) ; +} diff --git a/presets/nes/siegegame.c b/presets/nes/siegegame.c index 7d69493e..6fd3e14a 100644 --- a/presets/nes/siegegame.c +++ b/presets/nes/siegegame.c @@ -6,10 +6,12 @@ #include "neslib.h" -//#link "tileset1.c" +// VRAM buffer module +#include "vrambuf.h" +//#link "vrambuf.c" -extern unsigned char palSprites[16]; -extern unsigned char TILESET[8*256]; +// link the pattern table into CHR ROM +//#link "chr_generic.s" #define COLS 32 #define ROWS 27 @@ -31,48 +33,13 @@ byte getchar(byte x, byte y) { return rd + 0x20; } -// VRAM UPDATE BUFFER - -byte updbuf[64]; -byte updptr = 0; - -void cendbuf() { - updbuf[updptr] = NT_UPD_EOF; -} - -void cflushnow() { - cendbuf(); - waitvsync(); - flush_vram_update(updbuf); - updptr = 0; - cendbuf(); - vram_adr(0x0); -} - -void vdelay(byte count) { - while (count--) cflushnow(); -} void cputcxy(byte x, byte y, char ch) { - word addr = NTADR_A(x,y); - if (updptr >= 60) cflushnow(); - updbuf[updptr++] = addr >> 8; - updbuf[updptr++] = addr & 0xff; - updbuf[updptr++] = ch - 0x20; - cendbuf(); + putbytes(NTADR_A(x,y), &ch, 1); } -void cputsxy(byte x, byte y, char* str) { - word addr = NTADR_A(x,y); - byte len = strlen(str); - if (updptr >= 60 - len) cflushnow(); - updbuf[updptr++] = (addr >> 8) | NT_UPD_HORZ; - updbuf[updptr++] = addr & 0xff; - updbuf[updptr++] = len; - while (len--) { - updbuf[updptr++] = *str++ - 0x20; - } - cendbuf(); +void cputsxy(byte x, byte y, const char* str) { + putbytes(NTADR_A(x,y), str, strlen(str)); } void clrscr() { @@ -133,7 +100,7 @@ void draw_playfield() { cputcxy(9,1,players[0].score+'0'); cputcxy(28,1,players[1].score+'0'); if (attract) { - cputsxy(5,ROWS-1,"ATTRACT MODE - PRESS 1"); + cputsxy(3,ROWS-1,"ATTRACT MODE - PRESS ENTER"); } else { cputsxy(1,1,"PLYR1:"); cputsxy(20,1,"PLYR2:"); @@ -229,7 +196,8 @@ void flash_colliders() { //cv_set_attenuation(CV_SOUNDCHANNEL_0, i/2); if (players[0].collided) players[0].head_attr ^= 0x80; if (players[1].collided) players[1].head_attr ^= 0x80; - vdelay(2); + cflushnow(); + cflushnow(); draw_player(&players[0]); draw_player(&players[1]); } @@ -240,7 +208,7 @@ void make_move() { byte i; for (i=0; i= VBUFSIZE-4-len) cflushnow(); updbuf[updptr++] = (addr >> 8) ^ NT_UPD_HORZ; updbuf[updptr++] = addr & 0xff; diff --git a/presets/nes/vrambuf.h b/presets/nes/vrambuf.h index 04cf6df6..5f8fd68e 100644 --- a/presets/nes/vrambuf.h +++ b/presets/nes/vrambuf.h @@ -33,6 +33,6 @@ void cflushnow(void); // add multiple characters to update buffer // using horizontal increment -void putbytes(word addr, char* str, byte len); +void putbytes(word addr, const char* str, byte len); #endif // vrambuf.h diff --git a/presets/nes/vrambuffer.c b/presets/nes/vrambuffer.c new file mode 100644 index 00000000..71119c15 --- /dev/null +++ b/presets/nes/vrambuffer.c @@ -0,0 +1,59 @@ + +#include "neslib.h" +#include +#include + +// VRAM buffer module +#include "vrambuf.h" +//#link "vrambuf.c" + +// link the pattern table into CHR ROM +//#link "chr_generic.s" + +// function to scroll window up and down until end +void scroll_demo() { + int x = 0; // x scroll position + int y = 0; // y scroll position + int dy = 1; // y scroll direction + // 32-character array for string-building + char str[32]; + // clear string array + memset(str, 0, sizeof(str)); + // infinite loop + while (1) { + // write message to string array + sprintf(str, "%6x %6d", y, y); + // write string array into VRAM buffer + putbytes(NTADR_A(2,y%30), str, 32); + // wait for next frame + // and flush VRAM buffer + cflushnow(); + // set scroll register + scroll(x, y); + // update y variable + y += dy; + // change direction when hitting either edge of scroll area + if (y >= 479) dy = -1; + if (y == 0) dy = 1; + } +} + +// main function, run after console reset +void main(void) { + // set palette colors + pal_col(1,0x04); + pal_col(2,0x20); + pal_col(3,0x30); + + // clear vram buffer + cclearbuf(); + + // set NMI handler + set_vram_update(updbuf); + + // enable PPU rendering (turn on screen) + ppu_on_all(); + + // scroll window back and forth + scroll_demo(); +} diff --git a/src/platform/nes.ts b/src/platform/nes.ts index 2d8732ee..aa494da1 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -16,14 +16,15 @@ const JSNES_PRESETS = [ {id:'ex4.asm', name:'Controller Demo (ASM)'}, {id:'neslib1.c', name:'Text'}, {id:'scroll.c', name:'Scrolling'}, - {id:'statusbar.c', name:'Status Bar'}, - {id:'horizmask.c', name:'Horizontal Scrolling'}, + {id:'vrambuffer.c', name:'VRAM Buffer'}, {id:'sprites.c', name:'Sprites'}, {id:'metasprites.c', name:'Metasprites'}, {id:'flicker.c', name:'Flickering Sprites'}, {id:'metacursor.c', name:'Controllers'}, {id:'metatrigger.c', name:'Trigger Mode + Vbright'}, {id:'neslib5.c', name:'RLE Unpack'}, + {id:'statusbar.c', name:'Split Status Bar'}, + {id:'horizmask.c', name:'Horizontal Scrolling'}, {id:'music.c', name:'Music Player'}, {id:'siegegame.c', name:'Siege Game'}, {id:'shoot2.c', name:'Solarian Game'}, diff --git a/src/ui.ts b/src/ui.ts index 63326781..7d232c12 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -32,7 +32,7 @@ var toolbar = $("#controls_top"); export var current_project : CodeProject; // current CodeProject object -var projectWindows : ProjectWindows; // window manager +export var projectWindows : ProjectWindows; // window manager var stateRecorder : StateRecorderImpl; diff --git a/src/views.ts b/src/views.ts index 20c64009..a5ef78af 100644 --- a/src/views.ts +++ b/src/views.ts @@ -7,7 +7,7 @@ import { SourceFile, WorkerError, Segment } from "./workertypes"; import { Platform, EmuState } from "./baseplatform"; import { hex, lpad, rpad } from "./util"; import { CodeAnalyzer } from "./analysis"; -import { platform, platform_id, compparams, current_project, lastDebugState } from "./ui"; +import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows } from "./ui"; export interface ProjectView { createDiv(parent:HTMLElement, text:string) : HTMLElement; @@ -608,7 +608,11 @@ export class MemoryView implements ProjectView { $(parent).append(this.memorylist.container); this.tick(); if (compparams && this.dumplines) - this.memorylist.scrollToItem(this.findMemoryWindowLine(compparams.data_start)); + this.scrollToAddress(compparams.data_start); + } + + scrollToAddress(addr : number) { + this.memorylist.scrollToItem(this.findMemoryWindowLine(addr)); } refresh() { @@ -694,6 +698,7 @@ export class MemoryView implements ProjectView { return this.dumplines; } + // TODO: use segments list? getMemorySegment(a:number) : string { if (!compparams) return 'unknown'; if (a >= compparams.data_start && a < compparams.data_start+compparams.data_size) { @@ -805,6 +810,11 @@ export class MemoryMapView implements ProjectView { var row = $('
').append(offset, segdiv); var container = $('
').append(row); this.maindiv.append(container); + segdiv.click(() => { + var memview = projectWindows.createOrShow('#memory') as MemoryView; + memview.scrollToAddress(seg.start); + // TODO: this doesn't update nav bar + }); } refresh() { diff --git a/src/windows.ts b/src/windows.ts index de1ab784..377e851f 100644 --- a/src/windows.ts +++ b/src/windows.ts @@ -25,11 +25,11 @@ export class ProjectWindows { } // TODO: delete windows ever? - setCreateFunc(id:string, createfn:WindowCreateFunction) { + setCreateFunc(id:string, createfn:WindowCreateFunction) : void { this.id2createfn[id] = createfn; } - createOrShow(id:string) { + createOrShow(id:string) : ProjectView { var wnd = this.id2window[id]; if (!wnd) { wnd = this.id2window[id] = this.id2createfn[id](id); @@ -52,27 +52,27 @@ export class ProjectWindows { return wnd; } - put(id:string, window:ProjectView) { + put(id:string, window:ProjectView) : void { this.id2window[id] = window; } - refresh(moveCursor:boolean) { + refresh(moveCursor:boolean) : void { // refresh current window if (this.activewnd && this.activewnd.refresh) this.activewnd.refresh(moveCursor); } - tick() { + tick() : void { if (this.activewnd && this.activewnd.tick) this.activewnd.tick(); } - setErrors(errors:WorkerError[]) { + setErrors(errors:WorkerError[]) : void { this.lasterrors = errors; this.refreshErrors(); } - refreshErrors() { + refreshErrors() : void { if (this.activewnd && this.activewnd.markErrors) { if (this.lasterrors && this.lasterrors.length) this.activewnd.markErrors(this.lasterrors);