From a9bd84580037543a26abb598d221ebe29b9d3b5e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 17 Aug 2018 20:46:55 -0400 Subject: [PATCH] add path to (some) error msgs; fixed code analysis bugs (?) --- presets/nes/road.asm | 427 +++++++++++++++++++++++++++++++++++++++ src/analysis.ts | 62 ++++-- src/audio.ts | 8 +- src/baseplatform.ts | 1 + src/platform/nes.ts | 6 +- src/views.ts | 2 +- src/worker/workermain.js | 23 ++- test/cli/testworker.js | 42 ++-- 8 files changed, 518 insertions(+), 53 deletions(-) create mode 100644 presets/nes/road.asm diff --git a/presets/nes/road.asm b/presets/nes/road.asm new file mode 100644 index 00000000..3c6095fd --- /dev/null +++ b/presets/nes/road.asm @@ -0,0 +1,427 @@ + + include "nesdefs.asm" + +;;;;; ZERO-PAGE VARIABLES + + seg.u ZEROPAGE + org $0 + +TrackFrac .byte ; fractional position along track +Speed .byte ; speed of car +TimeOfDay .word ; 16-bit time of day counter +; Variables for preprocessing step +XPos .word ; 16-bit X position +XVel .word ; 16-bit X velocity +TPos .word ; 16-bit track position +TrackLookahead .byte ; current fractional track increment +; Variables for track generation +Random .byte ; random counter +GenTarget .byte ; target of current curve +GenDelta .byte ; curve increment +GenCur .byte ; current curve value + +ZOfs .byte ; counter to draw striped center line +Weather .byte ; bitmask for weather +Heading .word ; sky scroll pos. + +XCenter = 128 +NumRoadSegments = 28 + +; Preprocessing result: X positions for all track segments +RoadX0 ds NumRoadSegments + +; Generated track curve data +TrackLen equ 5 +TrackData ds TrackLen + +InitialSpeed equ 10 ; starting speed + +;;;;; OTHER VARIABLES + + seg.u RAM + org $300 + +;;;;; NES CARTRIDGE HEADER + + NES_HEADER 0,2,1,0 ; mapper 0, 2 PRGs, 1 CHR, horiz. mirror + +;;;;; START OF CODE + +Start: + NES_INIT ; set up stack pointer, turn off PPU + jsr WaitSync ; wait for VSYNC + jsr ClearRAM ; clear RAM + jsr WaitSync ; wait for VSYNC (and PPU warmup) + + jsr SetPalette ; set palette colors + jsr FillVRAM ; set PPU video RAM + + jsr RoadSetup + + lda #0 + sta PPU_ADDR + sta PPU_ADDR ; PPU addr = $0000 + sta PPU_SCROLL + sta PPU_SCROLL ; scroll = $0000 + lda #CTRL_NMI + sta PPU_CTRL ; enable NMI + lda #MASK_BG|MASK_SPR + sta PPU_MASK ; enable rendering + +.endless + jmp .endless ; endless loop + +;;;;; ROAD SUBS + +RoadSetup: subroutine + lda #1 + sta Random + lda #InitialSpeed + sta Speed + lda #0 + sta TimeOfDay+1 + lda #0 + sta Weather + rts + +RoadPreSetup: subroutine +; Set up some values for road curve computation, +; since we have some scanline left over. + lda #0 + sta XVel + sta XVel+1 + sta XPos + sta XPos+1 + lda TrackFrac + sta TPos + lda #0 + sta TPos+1 + lda #10 ; initial lookahead + sta TrackLookahead + rts + +RoadPostFrame: subroutine +; Advance position on track +; TrackFrac += Speed + lda TrackFrac + clc + adc Speed + sta TrackFrac + bcc .NoGenTrack ; addition overflowed? + jsr GenTrack ; yes, generate new track segment +.NoGenTrack +; TimeOfDay += 1 + inc TimeOfDay + bne .NoTODInc + inc TimeOfDay+1 + lda TimeOfDay+1 +; See if it's nighttime yet, and if the stars come out + clc + adc #8 + and #$3f + cmp #$35 + ror + sta Weather +.NoTODInc + lda Heading + sec + sbc XPos+1 + sta Heading + bit XPos+1 + bmi .NegHeading + lda Heading+1 + sbc #0 + sta Heading+1 + rts +.NegHeading + lda Heading+1 + sbc #$ff + sta Heading+1 + rts + +; Compute road curve from bottom of screen to horizon. +PreprocessCurve subroutine + ldx #NumRoadSegments-1 +.CurveLoop +; Modify X position +; XPos += XVel (16 bit add) + lda XPos + clc + adc XVel + sta XPos + lda XPos+1 + adc XVel+1 + sta XPos+1 + sta RoadX0,x ; store in RoadX0 array +; Modify X velocity (slope) +; XVel += TrackData[TPos] + ldy TPos+1 + lda TrackData,y + clc ; clear carry for ADC + bmi .CurveLeft ; track slope negative? + adc XVel + sta XVel + lda XVel+1 + adc #0 ; carry +1 + jmp .NoCurveLeft +.CurveLeft + adc XVel + sta XVel + lda XVel+1 + sbc #0 ; carry -1 + nop ; make the branch timings are the same +.NoCurveLeft + sta XVel+1 +; Advance TPos (TrackData index) +; TPos += TrackLookahead + lda TPos + clc + adc TrackLookahead + sta TPos + lda TPos+1 + adc #0 + sta TPos+1 +; Go to next segment + inc TrackLookahead ; see further along track + dex + bpl .CurveLoop + rts + +; Generate next track byte +GenTrack subroutine +; Shift the existing track data one byte up +; (a[i] = a[i+1]) + ldx #0 +.ShiftTrackLoop + lda TrackData+1,x + sta TrackData,x + inx + cpx #TrackLen-1 + bne .ShiftTrackLoop +; Modify our current track value and +; see if it intersects the target value + lda GenCur + clc + adc GenDelta + cmp GenTarget + beq .ChangeTarget ; target == cur? + bit GenTarget ; we need the sign flag + bmi .TargetNeg ; target<0? + bcs .ChangeTarget ; target>=0 && cur>=target? + bcc .NoChangeTarget ; branch always taken +.TargetNeg + bcs .NoChangeTarget ; target<0 && cur target + cmp GenCur + bmi .TargetBelow ; current > target? + lda Random + jsr NextRandom ; get a random value + sta Random + and #$f ; mask to 0..15 + jmp .TargetAbove +.TargetBelow + lda Random + jsr NextRandom + sta Random + ora #$f0 ; mask to -16..0 +.TargetAbove + ora #1 ; to avoid 0 values + sta GenDelta ; -> delta + lda GenCur +.NoChangeTarget +; Store the value in GenCur, and also +; at the end of the TrackData array + sta GenCur + sta TrackData+TrackLen-1 + rts + + +; fill video RAM +FillVRAM: subroutine + txa + ldy #$20 + sty PPU_ADDR + sta PPU_ADDR + ldy #$10 +.loop: + sta PPU_DATA + adc #7 + inx + bne .loop + dey + bne .loop + rts + +; set palette colors +SetPalette: subroutine + ldy #$00 + lda #$3f + sta PPU_ADDR + sty PPU_ADDR + ldx #32 +.loop: + lda Palette,y + sta PPU_DATA + iny + dex + bne .loop + rts + +; set sprite 0 +SetSprite0: subroutine + sta $200 ;y + lda #1 ;code + sta $201 + lda #0 ;flags + sta $202 + lda #8 ;xpos + sta $203 + rts + +;;;;; COMMON SUBROUTINES + + include "nesppu.asm" + +;;;;; INTERRUPT HANDLERS + + MAC SLEEP ;usage: SLEEP n (n>1) +.CYCLES SET {1} + + IF .CYCLES < 2 + ECHO "MACRO ERROR: 'SLEEP': Duration must be > 1" + ERR + ENDIF + + IF .CYCLES & 1 + bit $00 +.CYCLES SET .CYCLES - 3 + ENDIF + + REPEAT .CYCLES / 2 + nop + REPEND + ENDM + +NMIHandler: subroutine + SAVE_REGS +; setup sky scroll + lda Heading+1 + sta PPU_SCROLL + lda #0 + sta PPU_SCROLL +; load sprites + lda #112 + jsr SetSprite0 + lda #$02 + sta PPU_OAM_DMA +; do road calc + jsr RoadPreSetup + jsr PreprocessCurve + jsr RoadPostFrame +; wait for sprite 0 +.wait0 bit PPU_STATUS + bvs .wait0 +.wait1 bit PPU_STATUS + bvc .wait1 +; alter horiz. scroll position for each scanline + ldy #0 +.loop + tya + lsr + lsr + tax + lda RoadX0,x + sta PPU_SCROLL ; horiz byte + lda #0 + sta PPU_SCROLL ; vert byte + SLEEP 84 + iny + cpy #112 + bne .loop + RESTORE_REGS + rti + +;;;;; CONSTANT DATA + + align $100 +Palette: + hex 1f ;background + hex 09092c00 ;bg0 + hex 09091900 ;bg1 + hex 09091500 ;bg2 + hex 09092500 ;bg3 + +;;;;; CPU VECTORS + + NES_VECTORS + +;;;;; TILE SETS + + org $10000 +; background (tile) pattern table + REPEAT 10 +;;{w:8,h:8,bpp:1,count:48,brev:1,np:2,pofs:8,remap:[0,1,2,4,5,6,7,8,9,10,11,12]};; + hex 00000000000000000000000000000000 + hex 7e42424646467e007e42424646467e00 + hex 08080818181818000808081818181800 + hex 3e22023e30303e003e22023e30303e00 + hex 3c24041e06263e003c24041e06263e00 + hex 4444447e0c0c0c004444447e0c0c0c00 + hex 3c20203e06263e003c20203e06263e00 + hex 3e22203e26263e003e22203e26263e00 + hex 3e020206060606003e02020606060600 + hex 3c24247e46467e003c24247e46467e00 + hex 3e22223e060606003e22223e06060600 + hex 3c24247e626262003c24247e62626200 + hex 7c44447e62627e007c44447e62627e00 + hex 7e42406060627e007e42406060627e00 + hex 7e42426262627e007e42426262627e00 + hex 7c40407c60607c007c40407c60607c00 + hex 3c20203c303030003c20203c30303000 + hex 7e42406e62627e007e42406e62627e00 + hex 4242427e626262004242427e62626200 + hex 10101018181818001010101818181800 + hex 0404040606467e000404040606467e00 + hex 4444447e626262004444447e62626200 + hex 2020203030303e002020203030303e00 + hex fe9292d2d2d2d200fe9292d2d2d2d200 + hex 7e424262626262007e42426262626200 + hex 7e46464242427e007e46464242427e00 + hex 7e42427e606060007e42427e60606000 + hex 7e424242424e7e007e424242424e7e00 + hex 7c44447e626262007c44447e62626200 + hex 7e42407e06467e007e42407e06467e00 + hex 7e101018181818007e10101818181800 + hex 4242426262627e004242426262627e00 + hex 646464642c2c3c00646464642c2c3c00 + hex 4949494969697f004949494969697f00 + hex 4242423c626262004242423c62626200 + hex 4242427e181818004242427e18181800 + hex 7e42027e60627e007e42027e60427e00 + hex 10101818180018001010181818001800 + hex 187e407e067e1800187e407e067e1800 + hex 00180018180000000018001818000000 + hex 00003c3c0000000000003c3c00000000 + hex 00000018180000000000001818000000 + hex 18180810000000001818081000000000 + hex 00000018180810000000001818081000 + hex 7c7c7c7c7c7c7c007c7c7c7c7c7c7c00 + hex 0000000000007c000000000000007c00 + hex 00000000000000000000000000000000 + hex 00000000000000000000000000000000 +;; + REPEND + REPEAT 32 + hex 00000000000000000000000000000000 + REPEND diff --git a/src/analysis.ts b/src/analysis.ts index 73dd5c17..4756bdae 100644 --- a/src/analysis.ts +++ b/src/analysis.ts @@ -45,6 +45,7 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { WRAP_CLOCKS : boolean; jsrresult = {}; platform : Platform; + MAX_CYCLES : number = 2000; constructor(platform : Platform) { this.platform = platform; @@ -57,28 +58,45 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { } traceInstructions(pc:number, minclocks:number, maxclocks:number, subaddr:number, constraints) { + if (this.WRAP_CLOCKS) { + if (this.pc2minclocks[pc] !== undefined) + minclocks = Math.min(minclocks, this.pc2minclocks[pc]); + if (this.pc2maxclocks[pc] !== undefined) + maxclocks = Math.max(maxclocks, this.pc2maxclocks[pc]); + } //console.log("trace", hex(pc), minclocks, maxclocks); - if (!minclocks) minclocks = 0; - if (!maxclocks) maxclocks = 0; if (!constraints) constraints = {}; var modified = true; var abort = false; - for (var i=0; i<1000 && modified && !abort; i++) { + for (var i=0; modified && !abort; i++) { + if (i >= this.MAX_CYCLES) { + console.log("too many cycles @", hex(pc), "routine", hex(subaddr)); + break; + } modified = false; + if (this.WRAP_CLOCKS && minclocks >= this.MAX_CLOCKS) { + // wrap clocks + minclocks = minclocks % this.MAX_CLOCKS; + maxclocks = maxclocks % this.MAX_CLOCKS; + } else { + // truncate clocks + minclocks = Math.min(this.MAX_CLOCKS, minclocks); + maxclocks = Math.min(this.MAX_CLOCKS, maxclocks); + } var meta = this.getClockCountsAtPC(pc); var lob = this.platform.readAddress(pc+1); var hib = this.platform.readAddress(pc+2); var addr = lob + (hib << 8); var pc0 = pc; - if (!this.pc2minclocks[pc0] || minclocks < this.pc2minclocks[pc0]) { + if (!(minclocks >= this.pc2minclocks[pc0])) { this.pc2minclocks[pc0] = minclocks; modified = true; } - if (!this.pc2maxclocks[pc0] || maxclocks > this.pc2maxclocks[pc0]) { + if (!(maxclocks <= this.pc2maxclocks[pc0])) { this.pc2maxclocks[pc0] = maxclocks; modified = true; } - //console.log(hex(pc),minclocks,maxclocks,meta); + //console.log(hex(pc),minclocks,maxclocks,modified,meta,constraints); if (!meta.insnlength) { console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta); break; @@ -94,13 +112,21 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { meta.maxCycles -= 1; break; */ - // TODO: don't do in NES + // TODO: only VCS case 0x85: if (lob == 0x2) { // STA WSYNC minclocks = maxclocks = 0; meta.minCycles = meta.maxCycles = 0; } break; + // TODO: only NES (sprite 0 poll) + case 0x2c: + if (lob == 0x02 && hib == 0x20) { // BIT $2002 + minclocks = 0; + maxclocks = 4; // uncertainty b/c of assumed branch poll + meta.minCycles = meta.maxCycles = 0; + } + break; case 0x20: // JSR this.traceInstructions(addr, minclocks, maxclocks, addr, constraints); var result = this.jsrresult[addr]; @@ -115,6 +141,9 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { case 0x4c: // JMP pc = addr; // TODO: make sure in ROM space break; + case 0x40: // RTI + abort = true; + break; case 0x60: // RTS if (subaddr) { // TODO: 0 doesn't work // TODO: combine with previous result @@ -142,11 +171,13 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)]; var cons0 = constraintEquals(oldconstraints, cons[0]); var cons1 = constraintEquals(oldconstraints, cons[1]); - if (cons0 !== false) { + // recursively trace the taken branch + if (true || cons0 !== false) { // TODO? this.traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]); } + // abort if we will always take the branch if (cons1 === false) { - console.log("abort", hex(pc), oldconstraints, cons[1]); + console.log("branch always taken", hex(pc), oldconstraints, cons[1]); abort = true; } constraints = cons[1]; // not taken @@ -156,12 +187,9 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO return; } - minclocks = Math.min(this.MAX_CLOCKS, minclocks + meta.minCycles); - maxclocks = Math.min(this.MAX_CLOCKS, maxclocks + meta.maxCycles); - if (this.WRAP_CLOCKS && maxclocks >= this.MAX_CLOCKS) { - minclocks = minclocks % this.MAX_CLOCKS; - maxclocks = maxclocks % this.MAX_CLOCKS; - } + // add min/max instruction time to min/max clocks bound + minclocks += meta.minCycles; + maxclocks += meta.maxCycles; } } @@ -178,7 +206,7 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer { export class CodeAnalyzer_vcs extends CodeAnalyzer6502 { constructor(platform : Platform) { super(platform); - this.MAX_CLOCKS = this.START_CLOCKS = 76*2; + this.MAX_CLOCKS = this.START_CLOCKS = 76*2; // 2 scanlines this.WRAP_CLOCKS = false; } } @@ -188,7 +216,7 @@ export class CodeAnalyzer_vcs extends CodeAnalyzer6502 { export class CodeAnalyzer_nes extends CodeAnalyzer6502 { constructor(platform : Platform) { super(platform); - this.MAX_CLOCKS = 114; // ~341/3 + this.MAX_CLOCKS = 114; // 341 clocks for 3 scanlines this.START_CLOCKS = 0; this.WRAP_CLOCKS = true; } diff --git a/src/audio.ts b/src/audio.ts index c46c56bf..1b590b6f 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -306,11 +306,6 @@ var SampleAudio = function(clockfreq) { self.filterNode=self.context.createBiquadFilter(); self.filterNode.frequency.value=6000; - // "LED filter" at 3275kHz - off by default - self.lowpassNode=self.context.createBiquadFilter(); - self.lowpassNode.frequency.value=28867; - self.filter=false; - // mixer if ( typeof self.context.createScriptProcessor === 'function') { self.mixerNode=self.context.createScriptProcessor(self.bufferlen, 1, 1); @@ -326,8 +321,7 @@ var SampleAudio = function(clockfreq) { // patch up some cables :) self.mixerNode.connect(self.filterNode); - self.filterNode.connect(self.lowpassNode); - self.lowpassNode.connect(self.compressorNode); + self.filterNode.connect(self.compressorNode); self.compressorNode.connect(self.context.destination); } diff --git a/src/baseplatform.ts b/src/baseplatform.ts index d06d572f..2ada2aab 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -287,6 +287,7 @@ var OPMETA_6502 = { export function getOpcodeMetadata_6502(opcode, address) { // TODO: more intelligent maximum cycles + // TODO: must always be new object, b/c we might modify it return { opcode:opcode, minCycles:OPMETA_6502.cycletime[opcode], diff --git a/src/platform/nes.ts b/src/platform/nes.ts index b9c866ff..161bf49f 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -11,17 +11,17 @@ var JSNES_PRESETS = [ {id:'ex0.asm', name:'Initialization (ASM)'}, {id:'ex1.asm', name:'Scrolling Demo (ASM)'}, {id:'ex2.asm', name:'Sprite Demo (ASM)'}, -// {id:'hello.c', name:'C: Hello PPU'}, -// {id:'conio.c', name:'C: Hello Console I/O'}, {id:'neslib1.c', name:'Text'}, {id:'neslib2.c', name:'Sprites'}, {id:'neslib3.c', name:'Cursor'}, {id:'neslib4.c', name:'Metasprites'}, {id:'neslib5.c', name:'RLE Unpack'}, {id:'music.c', name:'Music Player'}, - {id:'musicdemo.asm', name:'Famitone Demo'}, {id:'siegegame.c', name:'Siege Game'}, {id:'shoot2.c', name:'Solarian Game'}, + {id:'scrollrt.asm', name:'Split Screen Scroll (ASM)'}, + {id:'road.asm', name:'3-D Road (ASM)'}, + {id:'musicdemo.asm', name:'Famitone Demo (ASM)'}, ]; var NES_NESLIB_PRESETS = [ diff --git a/src/views.ts b/src/views.ts index 2b2276dd..da298d17 100644 --- a/src/views.ts +++ b/src/views.ts @@ -216,7 +216,7 @@ export class SourceEditor implements ProjectView { setTimingResult(result:CodeAnalyzer) : void { this.editor.clearGutter("gutter-bytes"); // show the lines - for (const line in Object.keys(this.sourcefile.line2offset)) { + for (const line of Object.keys(this.sourcefile.line2offset)) { var pc = this.sourcefile.line2offset[line]; var minclocks = result.pc2minclocks[pc]; var maxclocks = result.pc2maxclocks[pc]; diff --git a/src/worker/workermain.js b/src/worker/workermain.js index 725c4e00..802ea059 100644 --- a/src/worker/workermain.js +++ b/src/worker/workermain.js @@ -399,7 +399,7 @@ var print_fn = function(s) { // test.c(6) : warning 85: in function main unreferenced local variable : 'x' // main.a (4): error: Unknown Mnemonic 'xxx'. // at 2: warning 190: ISO C forbids an empty source file -var re_msvc = /([^(]+)\s*[(](\d+)[)]\s*:\s*(.+?):\s*(.*)/; +var re_msvc = /[/]*([^( ]+)\s*[(](\d+)[)]\s*:\s*(.+?):\s*(.*)/; var re_msvc2 = /\s*(at)\s+(\d+)\s*(:)\s*(.*)/; function msvcErrorMatcher(errors) { @@ -409,7 +409,7 @@ function msvcErrorMatcher(errors) { var errline = parseInt(matches[2]); errors.push({ line:errline, - //path:matches[1], + path:matches[1], type:matches[3], msg:matches[4] }); @@ -419,13 +419,14 @@ function msvcErrorMatcher(errors) { } } -function makeErrorMatcher(errors, regex, iline, imsg) { +function makeErrorMatcher(errors, regex, iline, imsg, path) { return function(s) { var matches = regex.exec(s); if (matches) { errors.push({ line:parseInt(matches[iline]) || 1, - msg:matches[imsg] + msg:matches[imsg], + path:path }); } else { console.log("??? "+s); @@ -433,9 +434,9 @@ function makeErrorMatcher(errors, regex, iline, imsg) { } } -function extractErrors(regex, strings) { +function extractErrors(regex, strings, path) { var errors = []; - var matcher = makeErrorMatcher(errors, regex, 1, 2); + var matcher = makeErrorMatcher(errors, regex, 1, 2, path); for (var i=0; i:2: error: Can't open include file "foo.h" var errors = []; - var match_fn = makeErrorMatcher(errors, /:(\d+): (.+)/, 1, 2); + var match_fn = makeErrorMatcher(errors, /:(\d+): (.+)/, 1, 2, step.path); var MCPP = mcpp({ noInitialRun:true, noFSInit:true, @@ -1092,7 +1095,7 @@ function preprocessMCPP(step) { var errout = FS.readFile("mcpp.err", {encoding:'utf8'}); if (errout.length) { // //main.c:2: error: Can't open include file "stdiosd.h" - var errors = extractErrors(/[^:]+:(\d+): (.+)/, errout.split("\n")); + var errors = extractErrors(/[^:]+:(\d+): (.+)/, errout.split("\n"), step.path); if (errors.length == 0) { errors = [{line:0, msg:errout}]; } diff --git a/test/cli/testworker.js b/test/cli/testworker.js index d413c25a..d16818f9 100644 --- a/test/cli/testworker.js +++ b/test/cli/testworker.js @@ -15,29 +15,36 @@ global.onmessage({data:{preload:'sdcc'}}); // TODO: check msg against spec -function compile(tool, code, platform, callback, outlen, nlines, nerrors) { +function compile(tool, code, platform, callback, outlen, nlines, nerrors, options) { var msgs = [{code:code, platform:platform, tool:tool, path:'src.'+tool}]; - doBuild(msgs, callback, outlen, nlines, nerrors); + doBuild(msgs, callback, outlen, nlines, nerrors, options); } -function compileFiles(tool, files, platform, callback, outlen, nlines, nerrors) { +function compileFiles(tool, files, platform, callback, outlen, nlines, nerrors, options) { var msg = {updates:[], buildsteps:[]}; for (var fn of files) { var text = ab2str(fs.readFileSync('presets/'+platform+'/'+fn)); msg.updates.push({path:fn, data:text}); msg.buildsteps.push({path:fn, platform:platform, tool:tool}); } - doBuild([msg], callback, outlen, nlines, nerrors); + doBuild([msg], callback, outlen, nlines, nerrors, options); } -function doBuild(msgs, callback, outlen, nlines, nerrors) { +function doBuild(msgs, callback, outlen, nlines, nerrors, options) { var msgcount = msgs.length; global.postMessage = function(msg) { if (!msg.unchanged) { if (msg.errors && msg.errors.length) { - console.log(msg.errors); + for (var err of msg.errors) { + console.log(err); + assert.ok(err.line >= 0); + if (options && !options.ignoreErrorPath) { + assert.equal(msgs[0].path, err.path); + } + assert.ok(err.msg); + } assert.equal(nerrors, msg.errors.length, "errors"); } else { assert.equal(nerrors||0, 0, "errors"); @@ -97,7 +104,10 @@ describe('Worker', function() { compile('cc65', 'int main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', 'nes-conio', done, 0, 0, 1); }); it('should NOT compile CC65 (link error)', function(done) { - compile('cc65', 'extern void bad();\nint main() {\nbad();\nreturn 0;\n}', 'nes-conio', done, 0, 0, 1); + compile('cc65', 'extern void bad();\nint main() {\nbad();\nreturn 0;\n}', 'nes-conio', done, 0, 0, 1, {ignoreErrorPath:true}); + }); + it('should NOT compile CC65 (preproc error)', function(done) { + compile('cc65', '#include "NOSUCH.file"\n', 'nes-conio', done, 0, 0, 1, {ignoreErrorPath:true}); }); it('should assemble CA65', function(done) { compile('ca65', '\t.segment "HEADER"\n\t.segment "STARTUP"\n\t.segment "CHARS"\n\t.segment "VECTORS"\n\tlda #0\n\tsta $1\n', 'nes-conio', done, 40976, 2); @@ -116,8 +126,8 @@ describe('Worker', function() { it('should NOT assemble SDASZ80', function(done) { compile('sdasz80', '\txxx hl,#0\n\tret\n', 'mw8080bw', done, 0, 0, 1); }); - it('should NOT assemble SDASZ80', function(done) { - compile('sdasz80', '\tcall divxxx\n', 'mw8080bw', done, 0, 0, 1); + it('should NOT link SDASZ80', function(done) { + compile('sdasz80', '\tcall divxxx\n', 'mw8080bw', done, 0, 0, 1, {ignoreErrorPath:true}); }); it('should compile SDCC', function(done) { compile('sdcc', 'int foo=0; // comment\nint main(int argc) {\nint x=1;\nint y=2+argc;\nreturn x+y+argc;\n}\n', 'mw8080bw', done, 8192, 3, 0); @@ -167,6 +177,11 @@ describe('Worker', function() { }; doBuild(msgs, done2, 2799, 0, 0); }); + it('should NOT compile verilog example', function(done) { + var csource = "foobar"; + var msgs = [{code:csource, platform:"verilog", tool:"verilator", dependencies:[], path:'foomain.v'}]; + doBuild(msgs, done, 0, 0, 1); + }); it('should compile verilog inline assembler (JSASM)', function(done) { var csource = ab2str(fs.readFileSync('presets/verilog/racing_game_cpu.v')); var dependfiles = ["hvsync_generator.v", "sprite_bitmap.v", "sprite_renderer.v", "cpu8.v"]; @@ -280,14 +295,11 @@ describe('Worker', function() { "buildsteps":[ {"path":"main.c", "platform":"mw8080bw", "tool":"sdcc"}, {"path":"fn.c", "platform":"mw8080bw", "tool":"sdcc"} - ] + ], + "path":"fn.c" }; var msgs = [m]; - doBuild(msgs, function(err, result) { - for (var msg of result.errors) - assert.equal(msg.path, "fn.c"); - done(); - }, 8192, [1,1], 2); // TODO: check error file + doBuild(msgs, done, 8192, [1,1], 2); // TODO: check error file }); it('should compile vicdual skeleton', function(done) { var files = ['skeleton.sdcc', 'cp437.c'];