esop

Essential Stack-Operated Phone (concept)
git clone git://git.luxferre.top/esop.git
Log | Files | Refs | README | LICENSE

commit 61018ab06d16db370386948d77fc40a98c6e578d
parent 1d68fc8d4aa528808da18dda3570eaa2ceca5151
Author: Luxferre <3335173-suborg@users.noreply.gitlab.com>
Date:   Thu, 28 Jul 2022 22:55:10 +0300

Extended write hooks and debugged sound

Diffstat:
MREADME.md | 2+-
Mtal-lib/font.tal | 46++++++++++++++++++++++++++++++++++++++++++++++
Mweb/esop-ext.js | 36+++++++++++++++++++++---------------
Mweb/esop-web-app.js | 3++-
Mweb/uxncore.js | 25++++++++++++-------------
5 files changed, 82 insertions(+), 30 deletions(-)

diff --git a/README.md b/README.md @@ -53,7 +53,7 @@ Implementation note: direct writing to or reading from the control block memory All the 4032 pixels of 84x48 screen are allocated in the 504-byte video memory of ESOP (`#fe00-#fff7`) in the left-to-right, top-to-bottom, high-to-low order. To output a pixel at the given coordinates, the app must calculate the appropriate byte position itself. -The actual screen contents get updated 60 times per second according to the video memory. Before this process, the frame vector is run, if set in the corresponding control port (`00-01`). +The actual screen contents get updated 60 times per second according to the video memory. Before this process, the frame vector is run, if set in the corresponding control port (`00-01`). All graphics operations must be performed within this frame vector. Standard input/output ports are not supported. diff --git a/tal-lib/font.tal b/tal-lib/font.tal @@ -1,9 +1,55 @@ +( ESOP stdlib ) +|00 @Control [ &framevec $2 &input $2 &random $1 &status $1 &sound $1 &syscall $1 ] + +( byte to short ) +( x -- 00 x ) +%BTS { #00 SWP } + +( screen is #54 x #30 ) + +%SETSCRVEC { .Control/framevec DEO2 } +%RND { .Control/random DEI } +%STACKTRACE { #ff .Control/random DEO } +%SYS { .Control/syscall DEO } +%INKEY { .Control/input DEI2 } +%DBG { #20 SYS SYS } +%SND { .Control/sound DEO } + +( x y -- ref* ) +%COORD { BTS ROT BTS SWP2 #0054 MUL2 ADD2 } +( x y -- bitshift byteptr* ) +%PXLOFFSET { COORD DUP2 #03 SFT2 DUP2 #30 SFT2 ROT2 SWP2 SUB2 SWP POP ROT #fe00 ADD2 } + +( x y -- ) +%PXL { PXLOFFSET DUP2 LDA #01 ROT #40 SFT SFT ORA ROT ROT STA } ( subtract #20 from the ASCII code and you get the offset from the nanofont label ) %CHAR-TO-NF { #20 SUB ;nanofont ADD } +|0100 + #03 #02 #01 + ROT + DBG DBG DBG + ;on-frame SETSCRVEC + #30 + BRK + +@on-frame + DUP SND + INC + ( #10 #12 PXL ) +BRK + + +@putc + + JMP2r +@draw-str ( s* -> ) + &loop LDAk #00 EQU ,&eof JCN ( did we reach \0 ? ) + LDAk ;putc JSR2 INC2 ,&loop JMP ( no so emit a char and repeat ) + &eof POP2 JMP2r ( yes so return ) ( nanofont glyphs for ESOP, courtesy of Michaelangel007 ) diff --git a/web/esop-ext.js b/web/esop-ext.js @@ -44,13 +44,13 @@ function ESOPExtensions() { //setup screen renderer function renderScreen() { var vram = vm.getram(vramOffset, vramSize), - vdata = ctx.getImageData(0,0,width,height), + vdata = ctx.createImageData(width,height), ppos, pval, vbval, i, j - for(i=0;i<vRamSize;i++) { + for(i=0;i<vramSize;i++) { vbval = vram[i] for(j=0;j<8;j++) { //iterate from highest to lowest ppos = ((i<<3) + j) << 2 - pval = (vbval>>>(7-j))&1 ? 255 : 0 + pval = (vbval>>>(7-j))&1 ? 0 : 0xee vdata.data[ppos] = pval vdata.data[ppos+1] = pval vdata.data[ppos+2] = pval @@ -129,18 +129,24 @@ function ESOPExtensions() { function setupAudio(vm, actx) { var osc = null //keep the existing oscillator reference here vm.setdev(6,0) - vm.setWriteHook(6, function(port, portval) { - if(osc && !portval) { //stop the note - osc.stop() - osc.disconnect() - osc = null - } - else if(portval < sfl) { //start the note - osc = actx.createOscillator() - osc.type = 'square' - osc.frequency = soundFreqs[portval] - osc.connect(actx.destination) - osc.start(0) + vm.setWriteHook(6, function(port, portval, prev) { + if(portval !== prev) { + if(osc && !portval) { //stop the note + osc.stop() + osc.disconnect() + osc = null + } + if(portval && portval < sfl) { //start the note + if(osc) + osc.frequency.value = soundFreqs[portval] + else { + osc = actx.createOscillator() + osc.type = 'square' + osc.connect(actx.destination) + osc.frequency.value = soundFreqs[portval] + osc.start(0) + } + } } }) } diff --git a/web/esop-web-app.js b/web/esop-web-app.js @@ -2,7 +2,7 @@ function main() { var devCfg = { - audio: window.audioContext || window.webkitAudioContext, + audio: null, canvas: document.getElementById('C') //JS-specific implementation canvas DOM reference } @@ -13,6 +13,7 @@ function main() { rdr.onload = function() { var vm = UxnCore() var ext = ESOPExtensions() + devCfg.audio = new (window.AudioContext || window.webkitAudioContext) vm.boot() ext.setup(vm, devCfg) vm.load(new Uint8Array(rdr.result)) diff --git a/web/uxncore.js b/web/uxncore.js @@ -26,14 +26,9 @@ function UxnCore() { //helper methods function trapout(errcode) { //error reporting with vector execution - var vec = (mem.dev[0] << 8) | mem.dev[1] - if(vec) - uxnstart(vec) - else { - console.error('Error code: '+errcode+' at instruction #'+pc) - console.log('Working stack: ', mem.wst.dat) - console.log('Return stack: ', mem.rst.dat) - } + console.error('Error code: '+errcode+' at instruction #'+pc) + console.log('Working stack: ', mem.wst.dat) + console.log('Return stack: ', mem.rst.dat) pc = 0 } @@ -60,18 +55,22 @@ function UxnCore() { function PEEK(x) {return bs ? PEEK16(x) : mem.ram[x]} function DEVR(x) {if(x in devReadHooks) devReadHooks[x]();return bs ? (mem.dev[x] << 8)|mem.dev[x+1] : mem.dev[x]} function DEVW(x, y) { + var p, p2 if(bs) { + p = mem.dev[x] + p2 = mem.dev[x+1] mem.dev[x] = y>>8 if(x in devWriteHooks) - devWriteHooks[x](x, mem.dev[x]) + devWriteHooks[x](x, mem.dev[x], p) mem.dev[x+1] = y&255 if((x+1) in devWriteHooks) - devWriteHooks[x+1](x+1, mem.dev[x+1]) + devWriteHooks[x+1](x+1, mem.dev[x+1], p2) } else { + p = mem.dev[x] mem.dev[x] = y if(x in devWriteHooks) - devWriteHooks[x](x, y) + devWriteHooks[x](x, y, p) } } @@ -80,7 +79,7 @@ function UxnCore() { function uxnstart(startaddr, vmTrace) { pc = startaddr & 65535 var instr, src, dst, a, b, c, tmpTrace = !!vmTrace - if(!pc || mem.dev[0x0f]) { + if(!pc) { this.active = false return 0 } @@ -132,7 +131,7 @@ function UxnCore() { case 0x18: /* ADD */ a = POP(src); b = POP(src); PUSH(src, b + a); break; case 0x19: /* SUB */ a = POP(src); b = POP(src); PUSH(src, b - a); break; case 0x1a: /* MUL */ a = POP(src); b = POP(src); PUSH(src, b * a); break; - case 0x1b: /* DIV */ a = POP(src); b = POP(src); if(!a) trapout(3); PUSH(src, b / a); break; + case 0x1b: /* DIV */ a = POP(src); b = POP(src); if(!a) trapout(3); PUSH(src, 0|(b / a)); break; case 0x1c: /* AND */ a = POP(src); b = POP(src); PUSH(src, b & a); break; case 0x1d: /* ORA */ a = POP(src); b = POP(src); PUSH(src, b | a); break; case 0x1e: /* EOR */ a = POP(src); b = POP(src); PUSH(src, b ^ a); break;