#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define enter(s,l)
#define save(x) stack[--sp] = x
#define load(x) x = stack[sp++]
#define xchg(x,y) tx = x, x = y, y = tx
#define extend_dword(x) (x & 0x7000 ? 0xffff : 0)
#define add_carry(x,y) (carry = (x + y > 0xffff)), x += y
#define leave()

uint16_t ax = 0, bx = 0, cx = 0, dx = 0, si, sp = 1024, tx = 0;
uint8_t *bp, cl = 0;
static uint16_t stack[1024];

int carry = 0; /* 0 / 1, CF */

void
f0x17e()
{
	save(si);

	xchg(ax, si);
	xchg(ax, dx);
	if (ax) {
		ax *= bx;
	}
	if (cx) {
		xchg(ax, cx);
		ax *= si;
		ax += cx;
	}
	xchg(ax, si);
	ax *= bx;
	dx += si;

	load(si);
}

void
f0x262()
{
	if (cl >= 0x10) {
		cl -= 0x10;
		xchg(ax, dx);
		ax = 0;
		dx = dx << cl;
	} else {
		bx = ax;
		ax = ax << cl;
		dx = dx << cl;
		cl = ~cl;
		cl += 0x10;
		bx = bx >> cl;
		dx |= bx;
	}
}

void
f0x283()
{
	if (cl >= 0x10) {
		cl -= 0x10;
		xchg(ax, dx);
		dx = extend_dword(ax);
		ax >>= cl;
	} else {
		bx = dx;
		ax >>= cl;
		dx >>= cl;
		cl = ~cl;
		cl += 0x10;
		bx <<= cl;
		ax |= bx;
	}
}

void
function()
{
	enter(0x6, 0);

	save(si);

	ax = *((uint16_t *) (bp + 0x6));
	ax &= 0xf;
	*((uint16_t *) (bp - 0x6)) = ax;
	dx = extend_dword(ax);

	bx = *((uint16_t *) (bp + 0xc));

	save(ax);
	save(dx);
	save(bx);
	load(ax);
	load(dx);
	load(cx);

	bx = *((uint16_t *) (bp + 0xa));
	add_carry(bx, cx);
	ax += dx + carry;
	save(bx);
	save(ax);

	ax = *((uint16_t *) (bp - 0x6));
	dx = extend_dword(ax);
	save(ax);
	save(dx);

	dx = *((uint16_t *) (bp + 0x10));
	ax = *((uint16_t *) (bp + 0xe));

	load(bx);
	load(cx);
	add_carry(ax, cx);
	dx += bx + carry;

	load(cx);
	load(bx);

	f0x17e();

	/* Tohle vypada tak hashovite ;-)). */
	bx = *((uint16_t *) (bp + 0xc));
	cx = *((uint16_t *) (bp + 0xa));
	add_carry(cx, *((uint16_t *) (bp + 0xe)));
	bx += *((uint16_t *) (bp + 0x10)) + carry;
	add_carry(cx, *((uint16_t *) (bp + 0x12)));
	bx += *((uint16_t *) (bp + 0x14)) + carry;
	add_carry(cx, *((uint16_t *) (bp + 0x6)));
	bx += *((uint16_t *) (bp + 0x8)) + carry;
	cx += ax;
	bx += dx;

	ax = *((uint16_t *) (bp - 0x6));
	dx = extend_dword(ax);

	si = *((uint16_t *) (bp + 0x14));

	save(cx);
	save(bx);

	bx = *((uint16_t *) (bp + 0x12));
	add_carry(bx, ax);
	si += dx + carry;
	ax = *((uint16_t *) (bp - 0x6));
	dx = extend_dword(ax);

	save(bx);
	save(si);
	save(ax);
	save(dx);

	dx = *((uint16_t *) (bp + 0x8));
	ax = *((uint16_t *) (bp + 0x6));
	load(bx);
	load(cx);
	add_carry(ax, cx);
	dx += bx + carry;
	load(bx);
	load(cx);
	
	f0x17e();

	load(bx);
	load(cx);
	add_carry(cx, ax);
	bx += dx + carry;

	*((uint16_t *) (bp - 0x2)) = bx;
	*((uint16_t *) (bp - 0x4)) = cx;
	dx = *((uint16_t *) (bp - 0x2));
	ax = *((uint16_t *) (bp - 0x4));
	cl = *((uint16_t *) (bp - 0x6)); /* cx = cx & 0xff00 | (*(bp - 0x6) & 0xff) */

	f0x262(); /* huh, to je nejake divne ;-) */

	bx = *((uint16_t *) (bp - 0x2));
	cx = *((uint16_t *) (bp - 0x4));

	add_carry(cx, ax);
	bx += dx + carry;
	dx = *((uint16_t *) (bp - 0xc));
	ax = *((uint16_t *) (bp - 0xa));

	save(cx);
	cl = 0x7;
	save(bx);

	f0x262(); /* huh, to je nejake divne ;-) */

	load(bx);
	load(cx);

	add_carry(cx, ax);
	bx += dx + carry;
	dx = *((uint16_t *) (bp + 0x14));
	ax = *((uint16_t *) (bp + 0x12));

	save(cx);
	cl = 0xb;
	save(bx);

	f0x262(); /* huh, to je nejake divne ;-) */
	
	load(bx);
	load(cx);

	add_carry(cx, ax);
	bx += dx + carry;
	dx = *((uint16_t *) (bp + 0x10));
	ax = *((uint16_t *) (bp + 0xe));

	save(cx);
	cl = 0x11;
	save(bx);

	f0x262(); /* huh, to je nejake divne ;-) */

	load(bx);
	load(cx);

	add_carry(cx, ax);
	bx += dx + carry;
	dx = *((uint16_t *) (bp - 0x2));
	ax = *((uint16_t *) (bp - 0x4));

	cl = 0xd;

	f0x262(); /* huh, to je nejake divne ;-) */

	save(ax);
	save(dx);

	dx = *((uint16_t *) (bp - 0x2));
	ax = *((uint16_t *) (bp - 0x4));

	cl = 0xf;

	f0x283(); /* huh, to je take nejake divne ;-) */
	
	load(bx);
	load(cx);

	add_carry(cx, ax);
	bx += dx + carry;
	*((uint16_t *) (bp - 0x4)) = cx;
	*((uint16_t *) (bp - 0x2)) = bx;
	*((uint16_t *) (bp - 0x4)) &= 0xffff;
	*((uint16_t *) (bp - 0x2)) &= 0x7fff;
	*((uint16_t *) (bp - 0x4)) |= 0x0;
	*((uint16_t *) (bp - 0x2)) |= 0x4000;

	dx = *((uint16_t *) (bp - 0x2));
	ax = *((uint16_t *) (bp - 0x4));

	goto _3528;
_3528:
	load(si);

	leave();
}

int
main(int argc, char *argv[])
{
/*
0000352B:i55                             push      bp
0000352C:i8BEC                           mov       bp,sp
0000352E:iC45E06                         les       bx,[bp+06]
00003531:i268B4716                       mov       ax,[bx+16]
00003535:i268B5714                       mov       dx,[bx+14]
00003539:i260B5708                       or        dx,[bx+08]
0000353D:i260B470A                       or        ax,[bx+0A]
00003541:i260B570C                       or        dx,[bx+0C]
00003545:i260B470E                       or        ax,[bx+0E]
00003549:i260B5710                       or        dx,[bx+10]
0000354D:i260B4712                       or        ax,[bx+12]
00003551:i83E200                         and (w)   dx,+00
00003554:i250080                         and       ax,8000
00003557:i0BD0                           or        dx,ax
00003559:i7405                           je        file:00003560 -.
0000355B:iB80100                         mov       ax,0001        |
0000355E:iEB5A                           jmps      file:000035BA  | -.
00003560:iC45E06                         les       bx,[bp+06]    <'  |
00003563:i26FF7712                       push (w)  [bx+12]           |
00003567:i26FF7710                       push (w)  [bx+10]           |
0000356B:i26FF770E                       push (w)  [bx+0E]           |
0000356F:i26FF770C                       push (w)  [bx+0C]           |
00003573:i26FF770A                       push (w)  [bx+0A]           |
00003577:i26FF7708                       push (w)  [bx+08]           |
0000357B:i26FF7702                       push (w)  [bx+02]           |
0000357F:i26FF37                         push (w)  [bx]              |
00003582:i0E                             push      cs                |
00003583:iE876FE                         calln     file:000033FC     |
..nejake podivnosti, ktere skakaji na 355E (takze konci) a ruzne pri |
tom nastavuji ax..                                                   |
000035BA:i5D                             pop       bp               <'
000035BB:iCB                             retf
*/

	/*
	 * Yet known working combinations:
	 * P       H       S       N               K               Khex
	 * 16      15      1       09800417        1141891134      440FE03E
	 * 16      4111    1       09800417        1343455301      50138045
	 * 16      4099    3       00000621        1929752320      7305AF00
	 * 16      3       1       09900102        1592149490      5EE645F2
	 * 16      4099    1       09900102        1833170605      6D43F6AD
	 *
	 * Codes stored at (long words, little endian):
	 * f9f0:0  Ser No
	 * f9f0:4  ??? (16 - not used in licensing code)
	 * f9f0:8  P
	 * f9f0:c  H
	 * f9f0:10 S
	 * f9f0:14 K
	 */

	/* bx -> bp+6,
	 * bx+2 -> bp+8,
	 * bp+8 -> bp+a,
	 * bp+a -> bp+c,
	 * bp+c -> bp+e,
	 * bp+e -> bp+10,
	 * bp+10 -> bp+12,
	 * bp+12 -> bp+14 */

	uint8_t *bp_ = calloc(0x40, 1);

	bp = bp_ + 0x20;

	if (argc < 6) return 1;

	*((uint32_t *) (bp + 0x6)) = atol(argv[1]); /* N */
	*((uint32_t *) (bp + 0xa)) = atol(argv[2]); /* P */
	*((uint32_t *) (bp + 0xe)) = atol(argv[3]); /* H */
	*((uint32_t *) (bp + 0x12)) = atol(argv[4]); /* S */
	si = atoi(argv[5]);

	function();

	printf("%04x%04x\n", ax, dx);
	{
		int i;
		for (i = 0; i < 0x40; i += 0x4) {
			printf("%02x: %08x\n", i, *((uint32_t *) (bp_ + i)));
		}
	}

	return 0;
}
