mirror of
https://github.com/a2stuff/vnIIc.git
synced 2024-09-27 00:55:25 +00:00
Checkpoint - working
This commit is contained in:
parent
2425c89f7d
commit
6ac73d3b0e
@ -1,7 +1,6 @@
|
||||
|
||||
CC65 = ~/dev/cc65/bin
|
||||
CAFLAGS = --target apple2enh --list-bytes 0
|
||||
CCFLAGS = --config apple2-asm.cfg
|
||||
LDFLAGS = --config apple2-asm.cfg
|
||||
|
||||
TARGETS = \
|
||||
client.bin
|
||||
@ -17,7 +16,7 @@ clean:
|
||||
rm -f $(TARGETS)
|
||||
|
||||
%.o: %.s $(HEADERS)
|
||||
$(CC65)/ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $<
|
||||
|
||||
%.bin: %.o
|
||||
$(CC65)/ld65 $(CCFLAGS) -o $@ $<
|
||||
ld65 $(LDFLAGS) -o $@ $<
|
||||
|
Binary file not shown.
1326
client/client.list
1326
client/client.list
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,8 @@
|
||||
;;;
|
||||
;;;-------------------------------------------------------------------
|
||||
|
||||
PADDLE_SUPPORT = 1
|
||||
MOUSE_SUPPORT = 1
|
||||
PADDLE_SUPPORT = 0
|
||||
MOUSE_SUPPORT = 0
|
||||
|
||||
.include "apple2.inc"
|
||||
|
||||
@ -34,6 +34,7 @@ PTRIG := $C070
|
||||
|
||||
HCLR := $F3F2 ; Clear current hires screen to black
|
||||
|
||||
|
||||
;;;---------------------------------------------------------
|
||||
;;; Other
|
||||
;;;---------------------------------------------------------
|
||||
@ -125,7 +126,6 @@ PEXIT: .byte 0 ; Set when it's time to exit (Not Yet Implemente
|
||||
;;; bne :+ ; Nope
|
||||
|
||||
: jsr ReceivePage
|
||||
;; Input is sent every 256 bytes (32 times per page)
|
||||
jsr FlipHires
|
||||
|
||||
jmp :- ; TODO: define an exit trigger
|
||||
@ -142,11 +142,12 @@ PEXIT: .byte 0 ; Set when it's time to exit (Not Yet Implemente
|
||||
|
||||
ptr := $FA
|
||||
|
||||
.if 0
|
||||
lda #Protocol::Screen
|
||||
jsr SSC::Put
|
||||
lda #0 ; data size
|
||||
jsr SSC::Put
|
||||
|
||||
.endif
|
||||
|
||||
lda #0 ; set up write pointer
|
||||
sta ptr
|
||||
@ -157,11 +158,14 @@ ptr := $FA
|
||||
|
||||
: jsr SSC::Get
|
||||
sta (ptr),Y
|
||||
|
||||
iny
|
||||
bne :- ; Do a full page...
|
||||
|
||||
;; Interleave to maintain responsiveness
|
||||
.if 0
|
||||
jsr SendInputState
|
||||
.endif
|
||||
|
||||
inc ptr+1
|
||||
dex
|
||||
@ -350,12 +354,16 @@ done: rts
|
||||
sta PAGE
|
||||
jsr HCLR
|
||||
|
||||
jsr FlipHires ; then show it and flip to 2
|
||||
;; Show page 1
|
||||
sta HIRES
|
||||
sta TXTCLR
|
||||
sta MIXCLR
|
||||
sta LOWSCR
|
||||
|
||||
;; And set up writing to page 2
|
||||
lda #PAGE2
|
||||
sta PAGE
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
|
@ -64,9 +64,8 @@ BPSCTRL: .byte $16,$1E,$1F,$10 ; 300, 9600, 19200, 115k (with 8 data bi
|
||||
pha ; Push A onto the stack
|
||||
MOD_UASTAT_1 := *+1
|
||||
: lda UASTAT ; Check status bits
|
||||
and #$70
|
||||
cmp #$10
|
||||
bne :- ; Output register is full, so loop
|
||||
and #$10
|
||||
beq :- ; Output register is full, so loop
|
||||
pla
|
||||
MOD_UADATA_1 := *+1
|
||||
sta UADATA ; Put character
|
||||
@ -81,9 +80,8 @@ MOD_UADATA_1 := Put::MOD_UADATA_1
|
||||
.proc Get
|
||||
MOD_UASTAT_2 := *+1
|
||||
lda UASTAT ; Check status bits
|
||||
and #$68
|
||||
cmp #$8
|
||||
bne Get ; Input register empty, loop
|
||||
and #$8
|
||||
beq Get ; Input register empty, loop
|
||||
MOD_UADATA_2 := *+1
|
||||
lda UADATA ; Get character
|
||||
rts
|
||||
|
123
server.js
123
server.js
@ -27,7 +27,12 @@ $('#dither').addEventListener('input', e => {
|
||||
dither_factor = (input.value - input.min) / (input.max - input.min);
|
||||
});
|
||||
|
||||
// Holds last convert result
|
||||
let hires_buffer = new Uint8Array(8192);
|
||||
let dirty = true;
|
||||
|
||||
// Used during conversion
|
||||
let convert_buffer = new Uint8Array(8192);
|
||||
|
||||
// Save the last captured frame as a hires image file.
|
||||
$('#save').addEventListener('click', e => {
|
||||
@ -69,7 +74,9 @@ $('#start').addEventListener('click', async e => {
|
||||
const imagedata = ctx.getImageData(0, 0, can.width, can.height);
|
||||
|
||||
quantize(imagedata, indexes);
|
||||
convert_to_hires(indexes, hires_buffer);
|
||||
convert_to_hires(indexes, convert_buffer);
|
||||
hires_buffer.set(convert_buffer);
|
||||
dirty = true;
|
||||
|
||||
qctx.putImageData(imagedata, 0, 0);
|
||||
|
||||
@ -293,8 +300,25 @@ function convert_to_hires(indexes, buffer) {
|
||||
|
||||
let port;
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
$('#bootstrap').addEventListener('click', async e => {
|
||||
|
||||
port = await getSerialPort();
|
||||
|
||||
// Initial connection for bootstrapping
|
||||
await port.open({
|
||||
baudrate: 9600,
|
||||
databits: 8,
|
||||
parity: "none",
|
||||
stopbits: 1,
|
||||
rtscts: true
|
||||
});
|
||||
|
||||
alert('On the Apple II, type:\n\n' +
|
||||
' IN#2 (then press Return)\n' +
|
||||
' Ctrl+A 14B (then press Return)\n\n' +
|
||||
@ -303,11 +327,14 @@ $('#bootstrap').addEventListener('click', async e => {
|
||||
const CLIENT_ADDR = 0x6000;
|
||||
const CLIENT_FILE = 'client/client.bin';
|
||||
|
||||
port = getSerialPort();
|
||||
async function send(string) {
|
||||
await port.write(string + '\r');
|
||||
await sleep(100);
|
||||
}
|
||||
|
||||
await port.write('CALL -151'); // Enter Monitor
|
||||
await send('CALL -151'); // Enter Monitor
|
||||
|
||||
const response = await fetch(CLIENT_FILE);
|
||||
const response = await fetch(CLIENT_FILE, {cache: 'no-cache'});
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
const bytes = new Uint8Array(await response.arrayBuffer());
|
||||
@ -317,33 +344,53 @@ $('#bootstrap').addEventListener('click', async e => {
|
||||
[...bytes.slice(offset, offset + 8)]
|
||||
.map(b => ('00' + b.toString(16).toUpperCase()).substr(-2))
|
||||
.join(' ');
|
||||
addr += 8;
|
||||
|
||||
await port.write(str);
|
||||
await send(str);
|
||||
}
|
||||
|
||||
await port.write('\x03'); // Ctrl+C - Exit Monitor
|
||||
await port.write(`CALL ${CLIENT_ADDR}`); // Execute client
|
||||
await send('\x03'); // Ctrl+C - Exit Monitor
|
||||
await send(`CALL ${CLIENT_ADDR}`); // Execute client
|
||||
|
||||
// Bump connection to high speed
|
||||
await port.close();
|
||||
await port.open({
|
||||
baudrate: 115200,
|
||||
databits: 8,
|
||||
parity: "none",
|
||||
stopbits: 1,
|
||||
rtscts: true
|
||||
});
|
||||
|
||||
const splash = await fetch('res/SPLASH.PIC.BIN');
|
||||
if (!splash.ok)
|
||||
throw new Error(response.statusText);
|
||||
await port.write(new Uint8Array(await splash.arrayBuffer()));
|
||||
throw new Error(splash.statusText);
|
||||
await sleep(200); // Allow for app startup
|
||||
const img = await splash.arrayBuffer();
|
||||
hires_buffer.set(img);
|
||||
await port.write(img);
|
||||
});
|
||||
|
||||
|
||||
async function getSerialPort() {
|
||||
|
||||
const ports = await SerialPort.requestPorts();
|
||||
if (!ports.length) throw new Error('No ports');
|
||||
const port = new SerialPort(ports[0].path);
|
||||
const port = await navigator.serial.requestPort();
|
||||
if (!port) throw new Error('No ports');
|
||||
|
||||
const reader = port.in.getReader();
|
||||
const writer = port.out.getWriter();
|
||||
return {
|
||||
// Open port.
|
||||
open: async (options) => {
|
||||
await port.open(options);
|
||||
if (!port.readable) throw new Error('Port not readable');
|
||||
if (!port.writable) throw new Error('Port not writable');
|
||||
|
||||
this.reader = port.readable.getReader();
|
||||
this.writer = port.writable.getWriter();
|
||||
|
||||
// Generator yielding one byte at a time from |reader|.
|
||||
const gen = (async function*() {
|
||||
while (true) {
|
||||
const reader = this.reader;
|
||||
this.gen = (async function*() {
|
||||
while (port.readable) {
|
||||
const {value, done} = await reader.read();
|
||||
if (done) return;
|
||||
for (const byte of value)
|
||||
@ -351,12 +398,25 @@ async function getSerialPort() {
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
},
|
||||
|
||||
// Close port.
|
||||
close: async () => {
|
||||
if (this.reader) this.reader.cancel();
|
||||
if (this.writer) this.writer.releaseLock();
|
||||
|
||||
this.reader = null;
|
||||
this.writer = null;
|
||||
this.gen = null;
|
||||
|
||||
await port.close();
|
||||
},
|
||||
|
||||
// Read N bytes from port, returns plain array.
|
||||
read: async function read(n) {
|
||||
read: async (n) => {
|
||||
if (n <= 0) throw new Error();
|
||||
const result = [];
|
||||
for await (const byte of gen) {
|
||||
for await (const byte of this.gen) {
|
||||
result.push(byte);
|
||||
if (--n === 0) break;
|
||||
}
|
||||
@ -364,13 +424,13 @@ async function getSerialPort() {
|
||||
},
|
||||
|
||||
// Write Uint8Array of bytes to port.
|
||||
write: async function(bytes) {
|
||||
await writer.write(bytes);
|
||||
},
|
||||
write: async (bytes) => {
|
||||
if (!port.writable) throw new Error('Port not writable');
|
||||
if (typeof bytes === 'string') {
|
||||
bytes = new TextEncoder().encode(bytes);
|
||||
}
|
||||
|
||||
// Close port.
|
||||
close: async function() {
|
||||
await writer.close();
|
||||
await this.writer.write(bytes);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -383,6 +443,17 @@ async function getSerialPort() {
|
||||
|
||||
async function startStreaming() {
|
||||
|
||||
while (true) {
|
||||
await sleep(500);
|
||||
if (dirty) {
|
||||
// Send a copy
|
||||
let copy = new Uint8Array(hires_buffer);
|
||||
dirty = false;
|
||||
|
||||
await port.write(copy);
|
||||
}
|
||||
}
|
||||
|
||||
const state = {
|
||||
keyboard: 0,
|
||||
|
||||
@ -422,7 +493,7 @@ async function startStreaming() {
|
||||
case 0x32: state.mousebtn = data[0]; break;
|
||||
|
||||
// Screen
|
||||
case 0x80: port.write(hires_buffer); break;
|
||||
case 0x80: await port.write(hires_buffer); break;
|
||||
|
||||
default:
|
||||
console.warn(`Unexpected protocol command: ${command}`);
|
||||
|
Loading…
Reference in New Issue
Block a user