diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 070db9f..7cc77c0 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -17,9 +17,6 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - uses: webfactory/ssh-agent@v0.5.0 - with: - ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: npm install, build, and test run: | npm ci diff --git a/apple2js.html b/apple2js.html index 7eb7950..b6acb39 100644 --- a/apple2js.html +++ b/apple2js.html @@ -240,11 +240,15 @@
+
diff --git a/apple2jse.html b/apple2jse.html index fe2f208..1798561 100644 --- a/apple2jse.html +++ b/apple2jse.html @@ -249,11 +249,15 @@
+
diff --git a/js/gl.ts b/js/gl.ts index d35301b..1e1b217 100644 --- a/js/gl.ts +++ b/js/gl.ts @@ -517,6 +517,8 @@ export class HiresPageGL implements HiresPage { } } else if (bank === 0) { const hbs = val & 0x80; + const lastCol = col === 39; + const cropLastPixel = hbs && lastCol; const dx = col * 14; let offset = dx * 4 + dy * 560 * 4; if (hbs) { @@ -530,21 +532,24 @@ export class HiresPageGL implements HiresPage { } let bits = val; for (let idx = 0; idx < 7; idx++, offset += 8) { + const drawPixel = cropLastPixel && idx == 6 + ? this._drawHalfPixel + : this._drawPixel; if (bits & 0x01) { - this._drawPixel(data, offset, whiteCol); + drawPixel(data, offset, whiteCol); } else { - this._drawPixel(data, offset, blackCol); + drawPixel(data, offset, blackCol); } bits >>= 1; } + if (!this._refreshing) { + this._refreshing = true; + const after = addr + 1; + this._write(after >> 8, after & 0xff, this._buffer[0][after & 0x1fff], 0); + this._refreshing = false; + } } } - if (!this._refreshing && !doubleHiresMode && bank === 0) { - this._refreshing = true; - const after = addr + 1; - this._write(after >> 8, after & 0xff, this._buffer[0][after & 0x1fff], 0); - this._refreshing = false; - } } refresh() { diff --git a/js/ui/apple2.ts b/js/ui/apple2.ts index 8393fbf..8d2b958 100644 --- a/js/ui/apple2.ts +++ b/js/ui/apple2.ts @@ -49,6 +49,8 @@ type DiskCollection = { [name: string]: DiskDescriptor[] }; +const KNOWN_FILE_TYPES = [...DISK_FORMATS, ...TAPE_TYPES] as readonly string[]; + const disk_categories: DiskCollection = { 'Local Saves': [] }; const disk_sets: DiskCollection = {}; // Disk names @@ -239,7 +241,7 @@ export function loadAjax(drive: DriveNumber, url: string) { }); } -export function doLoad() { +export function doLoad(event: MouseEvent|KeyboardEvent) { MicroModal.close('load-modal'); const select = document.querySelector('#disk_select')!; const urls = select.value; @@ -255,7 +257,8 @@ export function doLoad() { const localFile = document.querySelector('#local_file')!; const files = localFile.files; if (files && files.length == 1) { - doLoadLocal(_currentDrive, files[0]); + const runOnLoad = event.shiftKey; + doLoadLocal(_currentDrive, files[0], { runOnLoad }); } else if (url) { let filename; MicroModal.close('load-modal'); @@ -317,7 +320,18 @@ function doLoadLocal(drive: DriveNumber, file: File, options: Partial('#local_file_address'); + const addressStr = addressInput?.value; + if (addressStr) { + const address = parseInt(addressStr, 16); + if (isNaN(address)) { + openAlert('Invalid address: ' + addressStr); + return; + } + doLoadBinary(file, { address, ...options }); + } else { + openAlert('Unknown file type: ' + ext); + } } } @@ -523,7 +537,8 @@ export function reset() { } function loadBinary(bin: JSONBinaryImage) { - for (let idx = 0; idx < bin.length; idx++) { + const maxLen = Math.min(bin.length, 0x10000 - bin.start); + for (let idx = 0; idx < maxLen; idx++) { const pos = bin.start + idx; cpu.write(pos, bin.data[idx]); } @@ -559,8 +574,8 @@ export function selectDisk() { localFile.value = ''; } -export function clickDisk() { - doLoad(); +export function clickDisk(event: MouseEvent|KeyboardEvent) { + doLoad(event); } /** Called to load disks from the local catalog. */ @@ -854,6 +869,22 @@ function onLoaded(apple2: Apple2, disk2: DiskII, smartPort: SmartPort, printer: _apple2.run(); }); } + + document.querySelector('#local_file')?.addEventListener( + 'change', + (event: Event) => { + const target = event.target as HTMLInputElement; + const address = document.querySelector('#local_file_address_input')!; + const parts = target.value.split('.'); + const ext = parts[parts.length - 1]; + + if (KNOWN_FILE_TYPES.includes(ext)) { + address.style.display = 'none'; + } else { + address.style.display = 'inline-block'; + } + } + ); } export function initUI(apple2: Apple2, disk2: DiskII, smartPort: SmartPort, printer: Printer, e: boolean) {