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:
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;