#include <stdio.h>
#include <assert.h>

#define MAX_LEVEL 128

int level=0;
char chain[MAX_LEVEL][16];
int call_type[MAX_LEVEL]; // 1 - far. 0 - near

void print_reg_and_chain (char *buf)
{
	int i;
	char tmp2[16];

	memcpy (tmp2, buf, 3+1+8);
	tmp2[3+1+8]=0;

	printf ("%s|", tmp2);
	for (i=0; i<level; i++)
		printf ("%s ", &chain[i][0]);
	printf ("\n");
};

FILE* f;

int main (int argc, char *argv[])
{
	char buf[10240];
	int cur_segm;
	int cur_EIP;
	char prevprev_ins[5];
	char prev_ins[5];

	assert (argv[1]!=NULL);

	f=fopen (argv[1], "rt");

	assert (f!=NULL);

	while(fgets(buf, 10240, f)!=NULL)
	{
		char tmp[16];
		int r;
		int i;

		assert (buf[4]==':');

		/*
		for (i=0; i<level; i++)
			printf (" ");
		printf ("buf: %s", buf);
		*/

		memcpy (tmp, buf, 4);
		tmp[4]=0;
		r=sscanf (tmp, "%X", &cur_segm);
		assert (r==1);

		memcpy (tmp, buf+4+1, 8);
		tmp[8]=0;
		r=sscanf (tmp, "%X", &cur_EIP);
		assert (r==1);

		if (memcmp (&buf[15], "pop  ", 5)==0)
		{
			if (memcmp (prev_ins, "call ", 5)==0)
			{
				// pop right after call

				if (level==0)
				{
					printf ("level=0!\n");
					exit(0);
				};
				level--;
			};
		}
		else
			if (memcmp (&buf[15], "call ", 5)==0)
			{
				if (buf[24]==':' || memcmp (&buf[20], "far", 3)==0) // something like "call 173A:016B" or "call far"
					call_type[level]=1;
				else
					call_type[level]=0;

				assert (level<MAX_LEVEL);
				if (level>=0)
					sprintf (&chain[level][0], "%X:%X", cur_segm, cur_EIP);
				level++;
			}
			else
				if (memcmp (&buf[15], "retf ", 5)==0)
				{
					if (memcmp (prev_ins, "push ", 5)==0)
					{
						// push/retf pair it is not retf, it is jump
					}
					else
					{
						if (level==0)
						{
							printf ("level=0!\n");
							exit(0);
						};
						level--;
						if (call_type[level]!=1)
						{
							printf ("(level=%d) call_type[level] is not 1!\n", level);
							exit (0);
						};
					};
				}
				else
					if (memcmp (&buf[15], "ret ", 4)==0 || 
						(memcmp (&buf[15], "iret ", 5)==0 && memcmp (prev_ins, "call ", 5)==0 && call_type[level-1]==0)
						)
					{
						if (level==0)
						{
							printf ("level=0!\n");
							exit(0);
						};
						level--;
						if (call_type[level]!=0)
						{
							printf ("(level=%d) call_type[level] is not 0!\n", level);
							exit (0);
						};
					}
					else
						if (memcmp (&buf[15], "out  dx,al", strlen ("out  dx,al"))==0)
						{
							printf ("OUT  |");
							print_reg_and_chain (buf+109); // EDX
						}
						else
							if (memcmp (&buf[15], "in   al,dx", strlen ("in   al,dx"))==0)
							{
								printf ("IN   |");
								print_reg_and_chain (buf+109); // EDX
							}
							else
								if (memcmp (&buf[15], "int  21", strlen ("int  21"))==0)
								{
									printf ("INT21|");
									print_reg_and_chain (buf+70); // EAX
								}
								else
								{
								};

		memcpy (prevprev_ins, prev_ins, 5);
		memcpy (prev_ins, &buf[15], 5);
	};
	fclose (f);
	printf ("(end) level=%d\n", level);
};
