commit:3985de1ac7c902eb0784699363c467f575ab7101
author:Chip
committer:Chip
date:Wed Apr 30 20:05:54 2025 -0500
parents:ae9c4240cbd79c930b43047610a9b47c6479d15f
Add examples
diff --git a/.gitignore b/.gitignore
line changes: +6/-0
index 245bdd9..c7b812f
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,11 @@
 *.TPU
 *.ppu
 *.PPU
+*.exe
+*.EXE
+*.map
+*.MAP
+*.bin
+*.BIN
 asm89
 asm89.exe

diff --git a/examples/bench/Makefile b/examples/bench/Makefile
line changes: +8/-0
index 0000000..b3508ab
--- /dev/null
+++ b/examples/bench/Makefile
@@ -0,0 +1,8 @@
+membench.exe: main.obj memcpy.obj
+	tlink /s main.obj+memcpy.obj+C:\TC\LIB\C0C.OBJ,membench.exe,membench.map,C:\TC\LIB\CC.LIB
+
+main.obj: main.c
+	tcc -c -mc -G -O -a -Z main.c
+
+memcpy.obj: memcpy.asm
+	asm89 memcpy.asm

diff --git a/examples/bench/i89.h b/examples/bench/i89.h
line changes: +21/-0
index 0000000..45d756b
--- /dev/null
+++ b/examples/bench/i89.h
@@ -0,0 +1,21 @@
+#ifndef _I89_H
+#define _I89_H
+
+/* This is intended to be the type for extern task block symbols. It's a little
+ * weird but you can't have an extern void value so it's an unsigned char. The
+ * CPU can't make sense of it anyway, so it may as well be a sequence of bytes
+ * to it. */
+typedef unsigned char i89_task_block;
+
+typedef struct i89_channel_parameter_block {
+	i89_task_block far * tbp;
+} i89_channel_parameter_block_t;
+
+typedef struct i89_channel {
+	unsigned char ccw;
+	unsigned char busy;
+	i89_channel_parameter_block_t far * ppb;
+	unsigned int reserved;
+} i89_channel_t;
+
+#endif /* _I89_H */

diff --git a/examples/bench/main.c b/examples/bench/main.c
line changes: +255/-0
index 0000000..ae0b5dc
--- /dev/null
+++ b/examples/bench/main.c
@@ -0,0 +1,255 @@
+#include <stdio.h>
+#include <dos.h>
+#include <conio.h>
+#include <alloc.h>
+#include <mem.h>
+
+#include "i89.h"
+
+extern i89_task_block tb_memcpy_mov, tb_memcpy_mov16, tb_memcpy_xfer, tb_memcpy_xfer16;
+
+struct memcpy_parameter_block {
+	i89_channel_parameter_block_t cpb;
+	unsigned int far * src;
+	unsigned int far * dest;
+	unsigned int count;
+};
+
+struct i89_channel far * ccb;
+struct memcpy_parameter_block memcpy_pb = { 0 };
+
+unsigned long int st_to_ticks(const struct time *t) {
+	return t->ti_hour * 360000 + t->ti_min * 6000 + t->ti_sec * 100 + t->ti_hund;
+}
+
+void memcpy_naive(void * dest, const void * src, size_t n) {
+	char * d = dest;
+	char * s = src;
+	while (n > 0) {
+		*d = *s;
+		d++;
+		s++;
+		n--;
+	}
+}
+
+void memcpy_naive16(void * dest, const void * src, size_t n) {
+	int * d = dest;
+	int * s = src;
+	n >>= 1;
+	while (n > 0) {
+		*d = *s;
+		d++;
+		s++;
+		n--;
+	}
+}
+
+void memcpy_i89(void * dest, const void * src, size_t n) {
+	memcpy_pb.cpb.tbp = &tb_memcpy_mov;
+	ccb[1].ccw = 0x03;
+	ccb[1].ppb = &memcpy_pb.cpb;
+	memcpy_pb.src = src;
+	memcpy_pb.dest = dest;
+	memcpy_pb.count = n;
+	outportb(0x72, 0);
+}
+
+void memcpy_i89_16(void * dest, const void * src, size_t n) {
+	memcpy_pb.cpb.tbp = &tb_memcpy_mov16;
+	ccb[1].ccw = 0x03;
+	ccb[1].ppb = &memcpy_pb.cpb;
+	memcpy_pb.src = src;
+	memcpy_pb.dest = dest;
+	memcpy_pb.count = n >> 1;
+	outportb(0x72, 0);
+}
+
+void memcpy_i89_xfer(void * dest, const void * src, size_t n) {
+	memcpy_pb.cpb.tbp = &tb_memcpy_xfer;
+	ccb[1].ccw = 0x03;
+	ccb[1].ppb = &memcpy_pb.cpb;
+	memcpy_pb.src = src;
+	memcpy_pb.dest = dest;
+	memcpy_pb.count = n;
+	outportb(0x72, 0);
+}
+
+void memcpy_i89_xfer_16(void * dest, const void * src, size_t n) {
+	memcpy_pb.cpb.tbp = &tb_memcpy_xfer16;
+	ccb[1].ccw = 0x03;
+	ccb[1].ppb = &memcpy_pb.cpb;
+	memcpy_pb.src = src;
+	memcpy_pb.dest = dest;
+	memcpy_pb.count = n;
+	outportb(0x72, 0);
+}
+
+void test_done(void * b1, void * b2, size_t len, const struct time * t0,
+               const struct time * t1, const char *test_name) {
+	long int t_acc = st_to_ticks(t1) - st_to_ticks(t0);
+	printf("%-20s%ld.%ld ticks\n", test_name, t_acc / 10, t_acc % 10);
+	if (memcmp(b1, b2, len) != 0) {
+		printf("data compare error\n");
+		exit(1);
+	}
+	memset(b2, 0, len);
+}
+
+void main() {
+	long int i;
+	size_t copy_len = 32768;
+	struct time t0, t1;
+	char far * buffer1;
+	char far * buffer2;
+
+	buffer1 = farmalloc(copy_len);
+	printf("buffer1: %Fp\n", buffer1);
+	if (buffer1 == NULL) {
+		printf("Could not allocate buffer1\n");
+		return;
+	}
+	buffer2 = farmalloc(copy_len);
+	printf("buffer2: %Fp\n", buffer2);
+	if (buffer2 == NULL) {
+		printf("Could not allocate buffer2\n");
+		return;
+	}
+	ccb = MK_FP(0x0000, 0x0500);
+
+	for (i = 0; i < copy_len; i++) {
+		buffer1[i] = (unsigned char)i;
+	}
+
+	gettime(&t0);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	memcpy(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy");
+
+	gettime(&t0);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	memcpy_naive(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_naive");
+
+	gettime(&t0);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	memcpy_naive16(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_naive16");
+
+	gettime(&t0);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	memcpy_i89(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_i89_mov");
+
+	gettime(&t0);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	memcpy_i89_16(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_i89_mov16");
+
+	gettime(&t0);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_i89_xfer");
+
+	gettime(&t0);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+	gettime(&t1);
+	test_done(buffer1, buffer2, copy_len, &t0, &t1, "memcpy_i89_xfer16");
+
+	printf("Looping tests...\n");
+
+	while (1) {
+		outportb(0x48, 0x01);
+		memcpy(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x02);
+		memcpy_naive(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x04);
+		memcpy_naive16(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x10);
+		memcpy_i89(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x20);
+		memcpy_i89_16(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x40);
+		memcpy_i89_xfer(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		outportb(0x48, 0x80);
+		memcpy_i89_xfer_16(buffer2, buffer1, copy_len);
+		outportb(0x48, 0);
+
+		kbhit();
+	}
+}

diff --git a/examples/bench/memcpy.asm b/examples/bench/memcpy.asm
line changes: +44/-0
index 0000000..c646d2a
--- /dev/null
+++ b/examples/bench/memcpy.asm
@@ -0,0 +1,44 @@
+	name memcpy
+	public _tb_memcpy_mov, _tb_memcpy_mov16, _tb_memcpy_xfer, _tb_memcpy_xfer16
+
+IOP	SEGMENT
+
+_tb_memcpy_mov:
+	lpd ga, [pp].4  ; source
+	lpd gb, [pp].8  ; dest
+	mov gc, [pp].12 ; len
+	movi ix, 0
+loop1:  movb [gb+ix+], [ga+ix]
+	dec gc
+	jnz gc, loop1
+	hlt
+
+_tb_memcpy_mov16:
+	lpd ga, [pp].4  ; source
+	lpd gb, [pp].8  ; dest
+	mov gc, [pp].12 ; len
+	movi ix, 0
+loop2:  mov [gb+ix+], [ga+ix]
+	dec gc
+	jnz gc, loop2
+	hlt
+
+_tb_memcpy_xfer:
+	lpd ga, [pp].4  ; source
+	lpd gb, [pp].8  ; dest
+	movi cc, 0C00Ch ; mem-mem, byte count termination
+	wid 8, 8
+	xfer
+	mov bc, [pp].12 ; len
+	hlt
+
+_tb_memcpy_xfer16:
+	lpd ga, [pp].4  ; source
+	lpd gb, [pp].8  ; dest
+	movi cc, 0C00Ch ; mem-mem, byte count termination
+	wid 16, 16
+	xfer
+	mov bc, [pp].12 ; len
+	hlt
+
+IOP	ENDS

diff --git a/examples/scanline/Makefile b/examples/scanline/Makefile
line changes: +8/-0
index 0000000..5983252
--- /dev/null
+++ b/examples/scanline/Makefile
@@ -0,0 +1,8 @@
+scanline.exe: main.obj iop.obj
+	tlink /s main.obj+iop.obj+C:\TC\LIB\C0C.OBJ,scanline.exe,scanline.map,C:\TC\LIB\CC.LIB
+
+main.obj: main.c
+	tcc -c -mc -G -O -a -Z main.c
+
+iop.obj: iop.asm
+	asm89 iop.asm

diff --git a/examples/scanline/i89.h b/examples/scanline/i89.h
line changes: +21/-0
index 0000000..45d756b
--- /dev/null
+++ b/examples/scanline/i89.h
@@ -0,0 +1,21 @@
+#ifndef _I89_H
+#define _I89_H
+
+/* This is intended to be the type for extern task block symbols. It's a little
+ * weird but you can't have an extern void value so it's an unsigned char. The
+ * CPU can't make sense of it anyway, so it may as well be a sequence of bytes
+ * to it. */
+typedef unsigned char i89_task_block;
+
+typedef struct i89_channel_parameter_block {
+	i89_task_block far * tbp;
+} i89_channel_parameter_block_t;
+
+typedef struct i89_channel {
+	unsigned char ccw;
+	unsigned char busy;
+	i89_channel_parameter_block_t far * ppb;
+	unsigned int reserved;
+} i89_channel_t;
+
+#endif /* _I89_H */

diff --git a/examples/scanline/iop.asm b/examples/scanline/iop.asm
line changes: +35/-0
index 0000000..0b20c11
--- /dev/null
+++ b/examples/scanline/iop.asm
@@ -0,0 +1,35 @@
+	name memcpy
+	public _tb_setup, _tb_frobline
+
+IOP	SEGMENT
+
+_tb_setup:
+	wid 16, 16
+	lpd ga, [pp].4  ; source
+	movi cc, 0800Ch ; port-block, byte count termination
+	movi ix, 8
+	hlt
+
+_tb_frobline:
+	lpd gb, [pp].8  ; dest                 11+3 fetch + 20 execute
+	xfer            ;                      7 fetch + 4 + 16*50 = 804 execute
+	movbi bc, 100   ; len                  14 fetch + 3 execute
+	mov gc, [pp].4  ; delay                11 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	addi ga, 2      ; next character       14 fetch + 3 execute              -- 958 cycles
+	lpd gb, [pp].8  ; dest                 11 fetch + 20 execute
+	xfer            ;                      7 fetch + 804 execute
+	movbi bc, 100   ; len                  14 fetch + 3 execute
+	addi ga, -2     ; previous character   11 fetch + 3 execute
+	dec ix          ;                      7 fetch + 3 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	mov gc, [pp]    ;                      7 fetch + 8 execute
+	jnz ix, _tb_frobline  ;                14 fetch + 5 execute              -- 962 cycles
+	hlt
+
+IOP	ENDS

diff --git a/examples/scanline/main.c b/examples/scanline/main.c
line changes: +96/-0
index 0000000..464cb4c
--- /dev/null
+++ b/examples/scanline/main.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <dos.h>
+#include <conio.h>
+#include <alloc.h>
+#include <mem.h>
+
+#include "i89.h"
+
+extern i89_task_block tb_setup, tb_frobline;
+
+struct frob_channel_parameter_block {
+	i89_task_block far * tbp;
+	unsigned int far * src;
+	unsigned int far * dest;
+	unsigned int count;
+};
+
+struct i89_channel far * ccb;
+struct frob_channel_parameter_block frob_pb = { 0 };
+unsigned int charptrs[2];
+
+unsigned long int st_to_ticks(const struct time *t) {
+	return t->ti_hour * 360000 + t->ti_min * 6000 + t->ti_sec * 100 + t->ti_hund;
+}
+
+char control(char dev, char com, char dat) {
+	struct REGPACK regs = { 0 };
+	regs.r_bx = dev;
+	regs.r_cx = com;
+	regs.r_dx = dat;
+	intr(0xFC, &regs);
+	return regs.r_ax;
+}
+
+void setup_chars() {
+	/* Set up two characters, one full white, one full black */
+	unsigned int * char_mem = MK_FP(0, 0x2800);
+	int i;
+	for (i = 0; i < 16; i++) {
+		char_mem[i] = 0xFFFF;
+	}
+	for (i = 0; i < 16; i++) {
+		char_mem[i + 16] = 0x0000;
+	}
+}
+
+void frob_setup() {
+	ccb[1].ccw = 0x03;
+	ccb[1].ppb = (i89_channel_parameter_block_t far *) &frob_pb;
+	frob_pb.tbp = &tb_setup;
+	frob_pb.src = &charptrs;
+	frob_pb.dest = MK_FP(0xF000, 0);
+	outportb(0x72, 0);
+	while (ccb[1].busy) {}
+	frob_pb.tbp = &tb_frobline;
+}
+
+void frob() {
+	ccb[1].ccw = 0x03;
+	outportb(0x72, 0);
+}
+
+void frob86(unsigned int * screenmem) {
+	int i;
+	for (i = 0; i < 50; i++) {
+		screenmem[i] = charptrs[0];
+	}
+	for (i = 0; i < 50; i++) {
+		screenmem[i] = charptrs[1];
+	}
+}
+
+void main() {
+	unsigned int far * screen_mem = MK_FP(0xF000, 0);
+	int x, y;
+
+	ccb = MK_FP(0, 0x500);
+	setup_chars();
+	control('1', 1, 1); /* set graphics mode */
+	for (y = 0; y < 25; y++) {
+		for (x = 0; x < 50; x++) {
+			screen_mem[y*50+x] = (0x2800 >> 5) + (y & 1);
+		}
+	}
+	charptrs[0] = (0x2800 >> 5);
+	charptrs[1] = (0x2820 >> 5);
+
+	frob_setup();
+	while(1) {
+		frob();
+		if (kbhit()) break;
+	}
+	getch();
+
+	control('1', 1, 0); /* reset text mode */
+}