[comp.sources.amiga] pstransformer

ain@j.cc.purdue.edu (Patrick White) (01/10/88)

Program Name:	pstransformer
Submitted By:	Phil Staub <phils@tekig.tek.com>
Summary:	This is a replacement for the file "transformer" on the
		transformer disk.  It solves the problem with extended
		memory among other things.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   Untested.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================


#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	PSTransformer.doc
#	PSTransformer.c
#	privhndlr.asm
#	start.asm
#	makefile
# This archive created: Thu Dec 17 23:01:12 1987
cat << \SHAR_EOF > PSTransformer.doc
		PSTransformer Version 1.0


Q: What is PSTransformer?

A: PSTransformer is a replacement for the file called "Transformer" on the 
AmigaTransformer distribution disk. It is distributed in source and binary
form in hopes of maximum distribution.



Q: Why should I use PSTransformer?

A: AmigaTransformer, as distributed, has 3 major problems. 

	1. It only works with version 1.1 of AmigaDos.
	2. It does not support expanded memory.
	3. It does not work if you have replaced your 68000
	   microprocessor with a 68010 or 68020.

PSTransformer attempts to solve these problems (and I hope is successful
in doing so. Early results seem to indicate it will work in far more 
cases than my original posting).


A little history. (Not for the feint of heart)

I made an earlier attempt to correct the first of these problems. At that 
time I didn't have either expanded memory or a 68010/020, so I was satisfied
with just making it work on 1.2. I posted that fix as ATPatch nearly a 
year ago.

Unfortunately, ATPatch made Transformer as specific to version 1.2 as it
originally was to 1.1. Also, neither of the other problems went away. So,
I set out to see what I could do to extend the function of the original 
patch to fix the other problems. 

Back in August, some of you may recall that I sent out a request for beta 
testers for a new version which hopefully would do just that. Having only 
the expansion memory which comes when you install the CMI KickStart 
Eliminator, (which is not auto-config) I felt it necessary to test it 
with some other configurations to see if it worked. 

Unfortunately, the answer was "no".

Back to the drawing board. Why didn't it work? I tried several things, and
eventually reached the following conclusion.

Transformer causes a "reset" instruction to be executed, to free up all 
of the memory except that which is absolutely needed by the basic services 
of the OS. This is because at the time Transformer was written, expansion
memory was either a) not widely available, and b) not auto-config.

To make sure that as few processes as possible are started up, thus using
memory which otherwise is available for DOS to use, a cool start vector 
is inserted which intercepts the boot-up sequence, apparently
before auto-config memory has been enabled. 

Before the reset, Transformer makes a list of all free memory, so that
it can re-build the free list when it gets control as a result of the 
cool reset. This works fine as long as memory is really at the addresses
in the free list before the reset. However, reset causes auto-config 
memory boards to become un-configured. Thus, the memory which we expect
to be there, isn't.

What options are there? Well, the first one was to find a way to re-
configure the auto-config memory. I was all for that one at first. 
Unfortunately, not having the 1.2 native developer update with auto-docs, 
and somehow not finding a way to spring free enough cash to get same,
I couldn't determine if there was a way to do it with an exec or 
expansion library call. And there simply wasn't enough room to patch
in a routine of my own to get the job done. Besides, remember I don't 
have any auto-config expansion memory to try it on. Any debugging of 
such a thing would have to be done by shipping a copy off to someone with
some expansion ram to have him/her try it, making the "test" phase of the
edit-compile-test-debug-edit cycle unacceptably long.

So I started searching for some other options. Finally, I began to wonder
if the reason for the reset was almost exclusively for the purpose
of freeing up memory. If so, I felt that, with the availability of extra 
memory these days being what it is, perhaps all I needed to do was to 
allocate memory out of what is available, up to the 640K that MSDOS 
allows.

Lo and behold, this was the key! Since a reset would never be executed,
auto-config memory would still be available. The only possible negative 
aspect here is users with minimum memory will not get as much memory 
for DOS use as would be possible when the reset is used. I strongly suspect
that this will become less and less of a problem as a greater and greater
percentage of Amiga owners have expanded memory.

Now, how to patch this into the Transformer emulator? I soon decided that
it would probably require more effort that the method I finally settled on:
a replacement startup module.




Q: How does PSTransformer work?

A: Basically, the original program "Transformer" was nothing more than 
a way to load the emulator, overlay the preferences from the 
ATPREFS file, then branch to the beginning of the segment 
containing the emulator. Memory allocation, the cool reset vector handler,
and the reset stuff were all in the emulator program. (Not to mention some
nasty stuff re-initializing some of the resources).

The intent of PSTransformer is to bypass the early phases of the emulator
startup, entering (with registers set appropriately) at some convenient 
point after all the mess previously pertaining to the reset is over.

PSTransformer now handles memory allocation in the startup module, as well
as overlaying the preferences. It also inserts a special priviledged 
instruction handler inspired by Scott Turner's DeciGel program (Thanks 
Scott!!) to allow the use of 68010 (and hopefully 68020) processors.
Then it branches off to a routine which sets up the registers
with emulator code, emulator data, and DOS workspace addresses and 
sizes, then enters the emulator. And away we go.

One other thing: I do something similar to what "FixHunk" does to ensure
that the emulator loads into CHIP ram. I found out the hard way that 
it must be there for MS-DOS to read 720K formatted disks in the outboard
drives. I don't understand why this is, and I'm not *that* anxious to find 
out.

Q: How do I use PSTransformer?

A: PSTransformer can be used from either WorkBench or CLI. 

First, of course, is to use a *copy* (not your original) AmigaTransformer
disk.

To use it from CLI, just put it on that disk, in the same directory as 
"Transformer", "AT1" and  "ATPREFS". Then type "PSTransformer".

I haven't provided an Icon to use from WorkBench. (This may sound silly,
but I've never made one! Kind of tells you how often I use WorkBench, 
doesn't it?) I would recommend that you either use the one that's already
there, renaming either the Icon or PSTransformer (no, I don't mind if you
rename my work), or build one yourself.


Q: How can I re-build the source?

A: PSTransformer was compiled, assembled and linked with the
Manx 3.40b package. A makefile is provided.


Q: Are there any problems with PSTransformer?

A: The question is bound to come up: 
	"Does PSTransformer work on the 500/2000?"

The answer is: "I'm not sure." I've got a report that DOS boots on a 
2000, but then the keyboard is locked out. I don't understand why. 
Perhaps that's one you 2000 hotshots can find for me. As for the 500, I have
no data.

Unfortunately, you don't get as much DOS ram available in a minimum
configuration (read: no expansion ram) as you used to. My suggestion 
here is to set up an extremely stripped down bootable disk and put the 
Transformer stuff on it. I mean *really* stripped down. 



Q: What about future versions?

A: I'm willing to support PSTransformer, if necessary, until (if?) the 
fixes are found for the suspected 2000 bug I mentioned above. Beyond that,
I'd have to see what kinds of support folks feel is necessary. This
project has taken up a major portion of my hacking time for a good
many months now, and, quite frankly, I'm tired of it, and I want to do
something else. Maybe I'll feel differently after a while.



Q: How about distribution?

A: I hereby grant authority for anyone to distribute this program in 
source and/or binary form. Period. I would appreciate getting credit
for my efforts, but since I'm distributing the source, there's not a lot
I can do to prevent you from removing my name. All I can say is to ask you
to please not do so.

In light of the questionable status of so called "copyrighted public domain"
software, I'm not putting any restrictions whatsoever on this. At least 
not for this version. Consider it my Christmas gift to all those on
Usenet who have provided me (sometimes without their knowledge) with 
a *lot* of information and wonderful programs.


If you have other questions or comments, you can reach me at:

	phils@tekigm2.TEK.COM

or my work or home address/phone:

	Phil Staub
Home:	14805 NE 77th St.
	Vancouver, Washington 98682
	(206) 254-9754

Office:	Tektronix, Inc.
	Accessories Engineering
	P.O. Box 3500, M/S C1-904
	Vancouver, Washington 98668
	(206) 253-5499
SHAR_EOF
cat << \SHAR_EOF > PSTransformer.c
#include	<stdio.h>
#include	<libraries/dos.h>
#include	<hardware/custom.h>
#include	<hardware/dmabits.h>
#include	<exec/types.h>
#include	<exec/memory.h>
#include	<exec/execbase.h>
#include	<functions.h>
#include	<exec/lists.h>
#include	<graphics/gfxmacros.h>


#define PREFERENCES_OFFSET	0x2   /* offset to an index to PREFS */
#define EMULATOR_ENTRY_POINT	0x298 /* actual start of the emulator */
#define DOSSIZE_OFFSET		0x3b  /* offset into ATPREFS for dossize */
#define PRIVHNDLRSIZE		0x38
#define DATASIZE		0xa3ce

#define HUNK_CODE		0x3e9L

char	*em = 0;		/* pointer to loaded emulator code */
struct Segment	*em_seg = 0;	/* pointer to loaded emulator segment */
char	*text = 0;		/* pointer to emulator text */
char	*data = 0;		/* pointer to emulator data */
char	*dos = 0;		/* pointer to area for dos */
char	*total_chip = 0;	/* pointer to a temp area for sizing */

long	tsize;			/* amount of memory for emulator text */
long	dsize = DATASIZE;	/* amount of memory for emulator data */
long	dossize;		/* amount of memory allocated to dos */ 
long	totalsize;		/* total text+data */

long	fsize;			/* amount of available fast memory */
long	csize;			/* amount of available chip memory */
long	size;			/* larger of free chip or fast mem */

long	tattributes;		/* AllocMem attributes for emulator text */
long	dattributes;		/* AllocMem attributes for user DOS space */

long	preferences_offset;	/* offset to data area for ATPREFS */
long	dossize_offset;		/* offset to ATPREFS requested dos size */
int	preferences_loaded;	/* flag to indicate that we read ATPREFS */

struct FileHandle *handle = 0;	/* file handle, general use */
char	pref_buf[200];		/* buffer to hold preferences data */

extern struct ExecBase	*SysBase;

main()
{
/* The declaration for PrivHndlr should really be a
 * function returning void, but we declare it this way because
 * we are copying the routine into a new place in RAM.
 */
	extern char	PrivHndlr[];	
	extern int	startup_text();

	char	s[10];	/* input buffer for testing for abort */

	/* The first thing we have to do is to ensure that the emulator
	 * loads into chip ram. This *could* have been done by 
	 * running "FixHunk" on the AT1 file, but since some people
	 * don't have that, we achieve the same result by writing 0x40 at 
	 * an offset of 0x14 bytes from the start of the file. This sets 
	 * bit 30 of the hunk size longword. 
	 *
	 * See page 283 of the Bantam AmigaDOS reference manual for more 
	 * details.
	 */
	handle = Open("AT1",(long)MODE_OLDFILE);
	if (!handle)
		errexit("Can't find AT1");

	if (Seek(handle,(long)0x14,(long)OFFSET_BEGINNING) == -1)
		errexit("File Seek error: AT1");

	/* we use the preferences buffer to hold the value to insert */
	pref_buf[0] = (char)0x40;
	if (Write(handle, &pref_buf[0], (long)1) == -1)
		errexit("File Write error: AT1");

	/* Now seek to the size of the text segment. */
	if (Seek(handle,(long)0x1c,(long)OFFSET_BEGINNING) == -1)
		errexit("File Seek error: AT1");

	/* See how big the text segment is. Use tsize as a 4 byte buffer,
	 * and read 4 bytes. This is the size of the segment in long 
	 * words. Adjust for a byte count, and add 8 bytes
	 * fudge factor for the segment overhead. (Size and 
	 * pointer to next)
	 */
	if (Read(handle,&tsize,(long)sizeof(long)) == -1)
		errexit("File Read error: AT1");
	Close(handle);
	handle = 0;

	tsize = (tsize << 2) + 8;	/* 8 bytes for segment overhead */


	/* Read the default Transformer preferences from ATPREFS */
	handle = Open("ATPREFS",(long)MODE_OLDFILE);
	if (!handle) {
		printf("Can't find ATPREFS. Using Default values.\n");
		dossize = 640 * 1024;
		preferences_loaded = 0;
	}
	else {
		Read(handle,pref_buf,200L);
		Close(handle);
		handle = 0;
		preferences_loaded = 1;

		/* see how much DOS space the user has requested */
		dossize = 1024 * (((pref_buf[DOSSIZE_OFFSET] & 0xff) << 8) +
				   (pref_buf[DOSSIZE_OFFSET+1] & 0xff)) + 62;
	}

	/* We don't allow interruptions from now on from other tasks */
	Forbid();

	/* make an unreasonably large memory allocation request to 
	 * flush any libraries out which are no longer in use.
	 */
	(void)AllocMem(0x1000000L, (long)MEMF_CHIP);
	(void)AllocMem(0x1000000L, (long)MEMF_FAST);


	/* We have to determine how much free memory we will
	 * have available for DOS. We need dsize + tsize bytes in 
	 * CHIP ram. Once that's obtained, we attempt to allocate the 
	 * amount of memory requested in ATPREFS for DOS. This can be 
	 * in either CHIP or FAST ram, depending on which we have more of.
	 */ 

	tsize = (tsize + 7) & ~7;	/* round up to 8 byte boundary */
	dsize = (dsize + 7) & ~7;	/* round up to 8 byte boundary */

	totalsize = tsize+dsize;

	/* allocate all we would need for both chip and text areas */
	total_chip = AllocMem(totalsize, (long)(MEMF_CHIP|MEMF_PUBLIC));

	/* we now need to see how much we have available for DOS */
	fsize = AvailMem((long)(MEMF_FAST|MEMF_LARGEST)) & ~07;
	csize = AvailMem((long)(MEMF_CHIP|MEMF_LARGEST)) & ~07;

	/* have to leave some chip ram for display use */
	csize -= 2048;

	/* If we have more FAST memory than we asked for for DOS use,
	 * or if we have more free FAST memory than CHIP, use FAST
	 * for DOS. Otherwise use CHIP.
	 */
	if ((fsize >= dossize) || (fsize >= csize)) {
		dattributes = MEMF_FAST|MEMF_PUBLIC;
		size = fsize;
	}
	else {
		dattributes = MEMF_CHIP|MEMF_PUBLIC;
		size = csize;
	}

	/* Free up the chunk we allocated above. We only allocated
	 * it to determine how much ram we would have left after
	 * allocation of space for emulator text and data.
	 */
	FreeMem(total_chip, totalsize);
	total_chip = NULL;

	/* Load the emulator segment */
	em_seg = LoadSeg("AT1");
	if (!em_seg)
		errexit("Can't load Emulator");

	/* Convert the BPTR to the loaded segment to a pointer,
	 * then increment it past the pointer to the "next" segment.
	 */
	em = (char *)BADDR(em_seg) + 4;


	/* If we were able to read ATPREFS, copy the preferences into 
	 * the appropriate place in the text area.
	 */
	if (preferences_loaded) {
		preferences_offset = *((long *)(&em[PREFERENCES_OFFSET]));
		movmem(pref_buf,&em[preferences_offset],(long)200);
	}

	/* We install a modified version of Scott Turner's DeciGel 
	 * program, which allows us to use a 68010 or 68020 processor.
	 */
	movmem(&PrivHndlr[0], 
	       &em[EMULATOR_ENTRY_POINT-PRIVHNDLRSIZE], (long)PRIVHNDLRSIZE);

	/* 'text' needs to point to the start of the emulator.
	 */
	text = &em[EMULATOR_ENTRY_POINT];

	/* now get some space for the emulator data */
	data = AllocMem(dsize, (long)MEMF_CHIP|MEMF_PUBLIC);
	if (!data)
		errexit("Can't get RAM for Emulator data\n");

	/* We'll only get the lesser of 'dossize' or 'size' ramspace
	 * for DOS.
	 */
	dossize = (dossize < size) ? dossize : size;
	dos = AllocMem(dossize,dattributes);

	/* Show us where we've allocated memory and how much of it there
	 * is. Also, a little ego boosting here.
	 */
	printf("\nPSTransformer, Version 1.0, 12/17/87 by Phil Staub\n\n");
	printf("Startup module for AmigaTransformer to provide support ");
	printf("for extended memory,\n");
	printf("68010/68020, and multiple operating system versions.\n\n");
	printf("\nAmigaTransformer Memory allocations\n");
	printf("Emulator code : %10ld bytes at %08lx\n", tsize, text);
	printf("Emulator data : %10ld bytes at %08lx\n", dsize, data);
	printf("DOS User Space: %10ld bytes at %08lx\n\n", dossize, dos);

	/* now offer the chance to bail out */

	printf("Continue? [y/n] ");
	gets(&s[0]);
	if (s[0] == 'n' || s[0] == 'N')
		errexit("AmigaTransformer exiting\n");

	OFF_SPRITE;			/* don't need the mouse pointer */
	OFF_DISPLAY; 			/* turn off the display dma */

	/* now branch off to an assembly language routine which
	 * installs the Privileged instruction  handler, sets up the 
	 * registers the way the emulator wants them, then starts the 
	 * emulator.
	 */
	startup_text();
}

errexit(s)
char	*s;
{
	printf("%s\n",s);
	if (em_seg)
		(void)UnLoadSeg((struct Segment *)em_seg);
	if (total_chip)
		FreeMem(total_chip, totalsize);
	if (data)
		FreeMem(data, dsize);
	if (dos)
		FreeMem(dos, dossize);
	if (handle)
		Close(handle);
	Permit();
	exit(1);
}
SHAR_EOF
cat << \SHAR_EOF > privhndlr.asm
	far	code
	far	data
	public	_PrivHndlr
PrivVect	equ	$20	; Address of Privlege error vector
;
;	This installs a handler for privilege violations
;	caused when a 68010 executes a "move sr,ea" instruction
;	in user mode.
;
;	It was inspired by Scott Turner's "DeciGEL" program.
;
;
;	code to handle move sr,ea instructions. this actually modifies 
;	code in place to a MOVE CCR,ea instruction, thus it is NO GOOD 
;	at all for code in ROM. (So big deal, eh?)
;
_PrivHndlr:
	movem.l	D0/A0,-(SP)		; Save registers
	move.l	10(SP),A0		; Pointer to opcode
	move.w	(A0),D0			; Pickup opcode
	andi.w	#$ffc0,D0		; Mask out EA field
	cmpi.w	#$40C0,D0		; Is it a MOVE SR,ea?
	bne	ReallyIllegal
	bset	#1,(A0)			; Convert it to MOVE CCR,ea
	movem.l	(sp)+,d0/a0		; restore regs
	rte				; Rerun new opcode

ReallyIllegal:
	movem.l	(sp)+,d0/a0		; restore regs
Prev:
	jmp	$FC0000			; To previous handler, patched on
					; installation of new handler

;
;	This is where we branch to start up Transformer
Emu_Start:
	lea	Prev+2(pc),a0		;save pointer to old handler
	move.l	PrivVect,(a0)
	lea	_PrivHndlr(pc),a0	;install new vector
	move.l	a0,PrivVect
;
;	We fall through to the beginning of the Transformer here
;
	end
SHAR_EOF
cat << \SHAR_EOF > start.asm
;
;	start.asm  - setup the starting condition of the registers
;			for AmigaTransformer.
;
;
;	We have the emulator copied into it's final resting position
;	and now we have to set up some of the registers as the emulator 
;	expects to find them. 
;
;	a3	base of emulator text area	(_text)
;	a4	base of emulator data area 	(_data)
;	a5	base of MS-DOS user area	(_dos)
;
;	d2	amount of data area used for emulator	(_dsize)
;	d3	amount of text area used for emulator	(_tsize)
;	d7	size of MS-DOS area			(_dossize)
;
	xdef	_startup_text
	xdef	_movmem

	xref	_text
	xref	_data
	xref	_dos
	xref	_dsize
	xref	_tsize
	xref	_dossize

PRIVHNDLRSIZE	equ	$38
INSTALLERSIZE	equ	$10

	cseg
_startup_text
	move.l	_text,a3
	move.l	_data,a4
	move.l	_dos,a5
	move.l	_dsize,d2
	move.l	_tsize,d3
	move.l	_dossize,d7

;	We actually start by branching off to the code to install 
;	the privileged instruction handler.
	jmp	-INSTALLERSIZE(a3)

;
;	movmem - move memory 
;
;	correctly handles overlapped moves
;
_movmem
	movem.l	4(sp),a0/a1
	move.l	12(sp),d0		;fetch parameters
	cmp.l	a0,a1			;see which direction to move
	beq.s	movend			; or if no move is necessary	
	bls	forward
	add.l	d0,a0			;move tail to tail
	add.l	d0,a1

reverse
	move.b	-(a0),-(a1)
	subq.l	#1,d0
	bne	reverse
movend
	rts
;
forward					;move head to head
	move.b	(a0)+,(a1)+
	subq.l	#1,d0
	bne	forward
	rts

	end
SHAR_EOF
cat << \SHAR_EOF > makefile
OBJS = PSTransformer.o privhndlr.o start.o

AFLAGS = -cd
CFLAGS = +p 

.asm.o:	
	as $(AFLAGS) -o $@ $*.asm

PSTransformer:	$(OBJS)
	ln +c $(OBJS) -o PSTransformer -lcl32
SHAR_EOF
#	End of shell archive
exit 0