* POKES.ASM - dangerous shell commands to examine and modify memory contents.
* Written in HiSoft Devpac 3.14, ver 1.5, March 1997; requires WB 2 or above.
* Copyright 1995-96 SNG, freely distributable; you use this at your own risk.

* Version 1.5 is a maintenance release for Aminet with minor source tweaks.

* Updates since version 1.3:
*
* Uses Tadek Knapik's AnyLongToString to write numbers in hex and binary.
* Appends a new line character to the output of PEEK.
* Allows BINARY as a synonym for BIN and H as a short form for HEX.
* Supports addresses and values in hex or binary as well as decimal.
* Closes DOS.LIBRARY even if READARGs fails.
* Suppresses leading zeroes in hexadecimal and binary output.
* Supports PEEK (but not POKE?) STRING argument.
* Supports AT for indirect addressing, to save extra PEEK LONGs.

* To do:
*
* Support for output of SIGNED values?
* Allow task=task-name argument to access task-relative data?

* Overview:
*
* These commands were written for three purposes:

* (1) To allow low-level access to hardware and memory, from the CLI.
* (2) To prevent the need to write little bits of code for each POKE.
* (3) To experiment with some non-trivial ReadArgs parameter parsing.
* (0) To annoy people who believe that POKEs are a thing of the past.

* Features:

* Can POKE or PEEK a byte, word or long word at any 32 bit memory address.
* Supports signed or unsigned decimal values, reduced MOD size if necessary. 
* Addressing can be relative to the base of any library, device or resource,
* or relative to CUSTOM (the base of the Amiga custom chips), CIAA or CIAB.
* Absolutely no protection so you can POKE anything anywhere. Caveat POKEr!

* Typical uses: in general these are not endorsed by Amiga Technology, and
* library, device and resource offsets can vary between software releases.
* These examples show the flexible format of various acceptable parameters:

* POKE ciaa 0 2		- Turns the audio filter off
* POKE CIAA 0 0		- Turns the audio filter on
* PEEK L 4			- Returns the EXEC library base address
* PEEK CUSTOM 18		- Controller 0 left paddle position
* PEEK CUSTOM 19                 - Controller 0 right paddle position
* PEEK Res potgo WORD 20	- Version number of POTGO.RESOURCE
* peek word lib dos 22	- Revision number of DOS.LIBRARY
* peek lib exec long 42	- Contents of EXEC cold-capture vector
* POKE CUSTOM WORD 48 511	- Sends MIDI RESET to the serial port
* POKE WORD dev="narrator" 48 90 - Sets speech rate to 90 words/minute
* peek LONG lib "graphics" 50	- Address of current Copper List
* POKE W DEV "narrator" 54 1     - Narrator attempts a female voice
* peek lib="exec" 62 long	- Returns the top of Chip Memory
* peek 62 word lib graphics	- Returns the current screen display mode
* poke w Dev narrator 62 16	- Set -12 dB Narrator speech volume
* PEEK W lib "graphics" 232	- Microseconds per scan line * 256
* peek lib graphics.library 236	- ChipSet revision, e.g. OCS/ECS/AGA  
* POKE CIAB 256 119		- Start motor of DF0, stop other drives
* poke 256 129 ciab		- Stop all floppy disk drive motors
* PEEK long library "exec" 276	- Returns base address of this task 
* peek LIBRARY="exec" w 296	- Returns ATTN_FLAGS (CPU type)
* POKE LIB "exec.library" 297 1	- Pretend this CPU is a 68010!
* Peek LIB "exec" 530		- Returns the vertical blanking frequency
* PEEK 531 lib exec		- Returns power supply frequency in Hertz
* PEEK LONG Library "exec" 568	- Number of timer 'E clocks' per second
* PEEK CIAB 2048		- Low byte of 24 bit scan-line counter
* PEEK dev=parallel string long 10 - "parallel.device", or "pit.device" if
*			MapDevice parallel 0 to pit 0 is active.

* See Mapping The Amiga, The Amiga Guru Book and the CBM Includes for lots
* more interesting and dangerous offsets. The best and most hazardous ones
* are secret. If you're not sure, PEEK first and POKE afterwards (perhaps)!

* Notes on parameter and result handling:

* You may specify the transfer size and device/resource/library/hardware
* base in any order, so PEEK WORD CUSTOM 18 means the same as PEEK 18 WORD
* CUSTOM or PEEK W 18 CUSTOM or PEEK CUSTOM WORD 18. The choice is yours.

* Unfortunately qualifiers (LIB, LIBRARY, RES, DEVICE etc) must appear just
* before the name of the library, resource or device which you want to use,
* so you need to say PEEK LIB exec 530 as PEEK exec LIB 530 will not work. 

* The Device option always selects unit zero, with 0 in the 'flags' field.

* Combinations of switches are additive, so PEEK LIB exec CUSTOM 2 reads
* the byte at ExecBase + $DFF000 + 2, which is unlikely to be very useful.

* The AT parameter indicates indirect addressing. The long word at the
* specified address is read and used as a pointer to the required data.
* Absolutely no validity checks are performed.

* Numeric values may be specified in decimal, binary with a % prefix, or
* hexadecimal if prefixed with $, 0X or 0x. Excessive values are reduced
* modulo the transfer size, e.g. POKE 0 $123 stores $23 and POKE WORD 0
* $123456 stores $3456. Thus conventional (decimal only) ReadArgs numeric
* parsing cannot be used for value and address arguments, so the /N does
* not appear in the argument template. Nonetheless, VALUE and ADDRESS
* arguments are numeric.

* By default PEEK returns a decimal number, but it can be a string up to
* 32 bytes long (stopping after 32 bytes or at the first null) returned
* in quotes, if the STRING switch is supplied, a value in binary, or in
* hexadecimal, indicated by the HEX or H switch and $ prefix before the
* number for base 16. The BIN or BINARY switch sets base 2 and a % prefix. 

* The STRING parameter expects that the argument is the offset of a pointer
* to the required string rather than the offset of the start of the string.

* Numeric parameters are 32 bit signed or unsigned decimal values. Results
* are unsigned bytes or words, signed long words. The default data size is
* BYTE - this is not a switch. Words and bytes are reduced modulo 65536 and
* modulo 256 respectively. Device, Resource and library names are case-
* sensitive and the .suffix is assumed if not explicitly presented. All
* other keywords are case-insensitive. Quotes, verbose qualifiers and
* equals signs are optional. PEEK returns an ASCII string followed by
* a trailing space and a newline, or nothing if an error occurs. You
* can  redirect this to  an environment variable and use its value later
* in your shell script. For instance, this code checks the current CPU
* and writes an appropriate message if it's a 68040 and 68060:

* PEEK lib=exec 297 >env:CPU
* IF $CPU equ 127
*   ECHO "68040 processor"
* ELSE
*   IF $CPU equ 255
*     ECHO "68060 processor"
*   ENDIF
* ENDIF

* EVAL can be used with PEEK environment variables. For instance this
* sequence finds the start address of any Commodore Kickstart ROM:

* PEEK LONG $FFFFEC >env:ROMsize
* EVAL 65536*256-$ROMsize

* There is currently no check for uneven-addressed words or long words,
* which cause an odd address guru on 68000/68010 systems but work fine
* on later processors, including all the current 32 bit Amiga models.

* Attempts to access non-existent memory on a Zorro 3 Amiga cause a bus
* error after a delay of a fraction of a second, unless remapped by MMU.

* Source organisation:

* No include files are needed as all necessary constants are in this file.
* This stand-alone source file includes the code for PEEK as well as POKE.
* Choose the one you want by setting PEEK equ 0 for POKE, PEEK=1 for PEEK.

PEEK	equ	1

MaxName	equ	64		Limit total length of name

* EXEC.LIBRARY jump table offsets

CloseLibrary equ	-414
OpenDevice	equ	-444
CloseDevice equ	-450
OpenResource equ	-498
OpenLibrary equ	-552	
 
* DOS.LIBRARY jump table offsets

ReadArgs	equ	-798
FreeArgs	equ	-858
VPrintf	equ	-954

* Offsets for ReadArgs results

WordSize	equ	0
LongSize	equ	4
LibName	equ	8
DevName	equ	12
ResName	equ	16
Custom	equ	20
CIAA	equ	24
CIAB	equ	28
AT	equ	32	Indirect address

	ifeq	PEEK

Address	equ	36
Value	equ	40

           else

Hex	equ	36	Return hexadecimal with $ prefix
Bin	equ	40	Return binary number with % prefix
String	equ	44	Return quoted ASCII string
Address	equ	48

	endc

	SECTION	INSTRUCTIONS,CODE

Start	move.l	4.w,a6
	move.l	a6,ExecBase
	moveq	#37,d0		Version 2.00 +
	lea.l	DosName,a1
	jsr	OpenLibrary(a6)

	move.l	d0,DosBase
	beq	Fail
	
	move.l	d0,a6
	move.l	#Template,d1
	lea.l	ArgTable,a2
	move.l	a2,d2
	moveq	#0,d3		No explicit RDARGS yet
	jsr	ReadArgs(a6)

	move.l	d0,d1
	beq	LateFail		** 1.4 ** 

	jsr	FreeArgs(a6)

	move.l	ExecBase,a6
	move.l	Address(a2),a0	Pointer to string
	jsr	AnyStrToLong
	tst.l	d1
	beq	LateFail
	
	move.l	d0,a3		Where to PEEK or POKE (or offset)

* Find library base if necessary

	move.l	LibName(a2),d0
	beq.s	NotLibrary

	lea.l	LibSuffix,a0
	bsr	NameCheck
	
	moveq	#0,d0		Any library version
	jsr	OpenLibrary(a6)	Call EXEC

	move.l	d0,CloseBase
	bne.s	AddBase
	
	bra	LateFail
	
* Find device base if necessary

NotLibrary	move.l	DevName(a2),d0
	beq.s	NotDevice	

	lea.l	DevSuffix,a0
	bsr	NameCheck
	
	move.l	a1,a0		Pointer to device name
	move.l	#ioRequest,a1
	moveq	#0,d0		Unit number
	moveq	#0,d1		No special flags
	jsr	OpenDevice(a6)
	
	tst.l	d0
	bne	LateFail

	move.l	#ioRequest,a1
	add.l	20(a1),a3		Add base of device structure
	move.l	a1,CloseBase	Store what thou shalt restore
	bra.s	NotResource

* Track down a resource, if necessary

NotDevice	move.l	ResName(a2),d0
	beq.s	NotResource

	lea.l	ResSuffix,a0
	bsr	NameCheck

	jsr	OpenResource(a6)	Call EXEC

	tst.l	d0		No need to store this one
	beq	LateFail
	
AddBase	add.l	d0,a3		Add base to supplied address

* If users mix and match switches, this code just totals ALL the offsets!

NotResource tst.l	Custom(a2)
	beq.s	NotCustom
	
	add.l	#$DFF000,a3
	
NotCustom	tst.l	CIAA(a2)
	beq.s	NotCIAA
	
	add.l	#$BFE001,a3
	
NotCIAA	tst.l	CIAB(a2)
	beq.s	NotCIAB
	
	add.l	#$BFD000,a3
		
NotCIAB	tst.l	AT(a2)
	beq.s	NotAT

	move.l	(a3),a3		Use indirect address
NotAT	
	ifeq	PEEK
	
	  move.l	Value(a2),a0	Where to PEEK what to POKE
	  jsr	AnyStrToLong
	  tst.l	d1
	  beq.s	LateFail
	
	else
	
	  moveq	#0,d0		Clear top bytes of result

	endc
	
	tst.l	WordSize(a2)
	bne.s	PokeWord
	
	tst.l	LongSize(a2)
	bne.s	PokeLong

	ifeq	PEEK		
	
	  move.b	d0,(a3)
	  bra.s	Done

PokeLong	  move.l	d0,(a3)		Must be word aligned if < 68020!
	  bra.s	Done
	
PokeWord	  move.w	d0,(a3)		Beware odd alignment on old CPUs

	else
	
	  move.b	(a3),d0
	  bra.s	Return

PokeLong	  move.l	(a3),d0		Must be even boundary if < 68020!
	  bra.s	Return
	
PokeWord	  move.w	(a3),d0		Odd alignment crashes old CPUs

* Send the result in D0 back to the shell with VPRINTF

Return	  lea.l	Buffer,a0

	  tst.l	String(a2)
	  beq.s    NotString
	 
	  move.l	d0,a1
	  moveq    #32-1,d1		Maximum to write to buffer
	  move.b   #'"',(a0)+
	  
CopyString	  move.b	(a1)+,(a0)+
	  dbeq     d1,CopyString
	  bne.s    Terminate    
	  subq.l   #1,a0                 Remove trailing null if any
Terminate	  move.b   #'"',(a0)+
	  clr.b    (a0)		Terminate with extreme prejudice
	  bra.s    Report
	  	  
	  
NotString	  move.l	#LTS_DECIMAL,d1
	  tst.l	Hex(a2)
	  beq.s	NotHex
	  
	  move.l	#LTS_HEX,d1
	  move.b	#'$',(a0)+
	  bra.s	Cooked

NotHex	  tst.l	Bin(a2)
	  beq.s	Cooked
	  
	  move.l	#LTS_BINARY,d1
	  move.b	#'%',(a0)+
*	  bra.s	Cooked
	  
Cooked	  jsr	AnyLongToStr
	  tst.l	d0
	  beq.s	LateFail
	  
Report	  move.l	#StrFormat,d1
             move.l	#Result,d2
             move.l	#Buffer,Result
             move.l	DosBase,a6
             jsr	VPrintf(a6)

	endc

* Close whatever library/device/resource we have opened before returning
				
Done	move.l	ExecBase,a6
	move.l	CloseBase,a1
	move.l	LibName(a2),d0
	beq.s	LibFree
	
	jsr	CloseLibrary(a6)

LibFree	move.l	DevName(a2),d0
	beq.s	CloseDOS
	
	jsr	CloseDevice(a6)
	
CloseDOS	move.l	DosBase,a1
	jsr	CloseLibrary(a6)
	
	moveq	#0,d0
	rts


* Direct exit from NameCheck if string is too long for our buffer

LaterFail	addq.l	#4,a7		Return to prior caller

* Exit used when the program fails after opening DOS.LIBRARY
	
LateFail	move.l	DosBase,a1
	move.l	ExecBase,a6
	jsr	CloseLibrary(a6)
	
Fail	moveq	#30,d0
	rts


* Ensure name string at D0 includes a dot, or add the suffix
* at A0. Return A1 pointing to the checked string. Uses A4,D4.

NameCheck	moveq	#MaxName-1,d1	-1 for DBEQ loops later
	lea.l	OpenName,a4
	move.l	d0,a1

Scan	move.b	(a1)+,d4
	cmp.b	#'.',d4
	beq.s	GotDot

	move.b	d4,(a4)+
	dbeq	d1,Scan               Continue till null or D1=-1

	bne.s	LaterFail

	subq.l	#1,a4		Step back over dot	
	addq.w	#1,d1		Adjust limit count

Append	move.b	(a0)+,(a4)+
	dbeq	d1,Append

	bne.s	LaterFail		We never reached the null!

	lea.l	OpenName,a1	Point at start of our copy
	rts
	
GotDot	move.l	d0,a1		Use the literal string
	rts

	SECTION	TADEK,CODE

**********************************************************************
*
*	AnyStrToLong - convert ASCII string to 32-bit value
*
* Input:  a0 - null terminated string
* Output: d0 - the value (was $FFFFFFFF if error - removed by SNG)
*         d1 - success (boolean)
*
*	As string you can pass decimal (no prefix), binary (prefix '%')
* or hex (prefixes '$' or '0x'). Check d1 for success or failure (fails
* when non-specific characters are found, or if null string).
*	In hex/bin mode, when you pass too many characters, only last
* 8/32 are used. In dec mode, maximal value is 4.294.967.295
* ($FFFFFFFF). Decimal values may be negative (prefix '-'), then the
* range is from -2.147.483.647 to 2.147.483.648 (otherwise the result
* is not reliable).
*
*	Alters only scratch registers (d0, d1, a0, a1). Uses 16 bytes
* of stack (decimal mode only).
*
*
*	© 1996 by Tadek Knapik (tadek@student.uci.agh.edu.pl).
*		 Public Domain. E-mail appreciated :-)
*
**********************************************************************



AnyStrToLong:
;;	cmpi.b	#0,(a0)		;null string?
	tst.b	(a0)		;; SNG - faster
	beq.s	AnyStrToLongError

	moveq	#0,d0		;negative flag in decimal mode

	cmpi.b	#'-',(a0)		;negative?
	beq	DecStrToLong0

	cmpi.b	#'%',(a0)		;binary?
	beq.s	BinStrToLong0	;yes!

	cmpi.b	#'$',(a0)		;hex
	beq.s	HexStrToLong0	;yes!

	cmpi.b	#'0',(a0)		;hex (0x)
	bne	DecStrToLong	;no!

	movea.l	a0,a1		;string pointer
	adda.l	#1,a1
	cmpi.b	#'X',(a1)		;; Capital check, SNG
	beq.s	Prefix0X
	
	cmpi.b	#'x',(a1)		;'x'?
	bne.s	DecStrToLong	;

Prefix0X	addq.l	#1,a0		;;omit '0' (Q not A)

;;	bra.s	HexStrToLong


;--------------------

;convert hex to long.

HexStrToLong0:
	addq.l	#1,a0		;;next byte (omit '$/x/X')

HexStrToLong:
	moveq	#0,d0
	moveq	#0,d1

HexNextNybble:
	tst.b	(a0)		;;the end?
	beq.s	AnyStrToLongEnd	;yes

;next character

	lsl.l	#4,d0		;prepare next 4 bits
;;	moveq	#0,d1		;clr = redundant, SNG
	move.b	(a0)+,d1		;next cipher

	cmpi.b	#'0',d1		;lower than 0?
	blt.s	AnyStrToLongError

	cmpi.b	#'f',d1		;higher than f?
	bgt.s	AnyStrToLongError

	subi.b	#'0',d1		;ASCII '0'
	bmi.s	AnyStrToLongError

;now we have 0-9. Or not..

	cmpi.b	#9,d1		;more than '9'?
	ble.s	HexReady		;no, we're done

	subq.b	#7,d1		;;try this

;If A-F, we have 10-15, unless <, >, @ etc., or a-f

	cmpi.b	#10,d1		;less than 'A'?
	blt.s	AnyStrToLongError	;yes, jump @ Error

	cmpi.b	#15,d1		;more than 'F'
	ble.s	HexReady		;OK

;so, probably a-f

	subi.b	#32,d1		;case change?

	cmpi.b	#10,d1		;less?
	blt.s	AnyStrToLongError	;yes, probably XYZ

	cmpi.b	#15,d1		;more than 'f'
	ble.s	HexReady		;no, OK
	bra.s	AnyStrToLongError	;error, probably xyz

;in d1 we have value in range 0-15

HexReady:
	or.b	d1,d0		;set those bits
	bra.s	HexNextNybble	;next one, please


AnyStrToLongError:
;;	moveq	#-1,d0		;;if -1 in d0, then 
	moveq	#0,d1		;d1 is boolean failure
	rts

AnyStrToLongEnd:
	moveq	#1,d1		
	rts

;--------------------

;convert binary to long.

BinStrToLong0:
	addq.l	#1,a0		;;next byte
BinStrToLong:
	tst.b	(a0)		;;nothing?
	beq.s	AnyStrToLongError

	moveq	#0,d0
	moveq	#0,d1

BinNextBit:
;;	cmpi.b	#0,(a0)		;coô jeszcze
	tst.b	(a0)		;;faster 
	beq	AnyStrToLongEnd

;what do we have here..

	lsl.l	#1,d0		;prepare next bit

	moveq	#0,d1
	move.b	(a0)+,d1

	subi.b	#'0',d1		
	bmi.s	AnyStrToLongError

	cmpi.b	#1,d1		;more than 1?
	bgt.s	AnyStrToLongError

;now we have 0 or 1 in d1.

	or.b	d1,d0		;to set or not to set
	bra.s	BinNextBit

;--------------------


;Convert decimal ASCII to 32 bit long.
;Maximum - 4.294.967.295 ($FFFFFFFF)
;If you pass in [x*4.294.967.295+y], it will probably return [y].


DecStrToLong0:
	addq.l	#1,a0		;;next byte (omit '-')
	moveq	#1,d0

DecStrToLong:
	tst.b	(a0)		;;nic?
	beq.s	AnyStrToLongError
	movem.l	d2-d5,-(sp)
	move.l	d0,d5		;negative flag

	moveq	#0,d1		;clr
	moveq	#0,d0		;the value
	movea.l	a0,a1		;string
	addq.l	#1,a1		;;cause I add immediately

;length of the string without last null character

DecStrLengthLoop:
	addq.l	#1,d1
	tst.b	(a1)+
	bne.s	DecStrLengthLoop	

;now in d1 we have the weight  of the first cipher plus 1

DecNextCipherLoop:
	subq.l	#1,d1		;sub 1

	tst.l	d1		;if null, this is the last one
	beq.s	DecLastCipher
	move.l	d1,d2

	moveq	#0,d3
	move.b	(a0)+,d3		;the cipher

	subi.b	#'0',d3		
	bmi.s	DecStrToLongError	

	cmpi.b	#9,d3		
	bgt.s	DecStrToLongError


DecMultipleLoop:
	move.l	d3,d4		;copy it

	lsl.l	#3,d3		;*8
	lsl.l	#1,d4		;*2
	add.l	d4,d3		;x*2+x*8=x*(2+8)=x*10

	subq.l	#1,d2
	bne.s	DecMultipleLoop

	add.l	d3,d0		
	bra.s	DecNextCipherLoop


DecLastCipher:
	moveq	#0,d3
	move.b	(a0)+,d3

	subi.b	#'0',d3	
	bmi.s	DecStrToLongError

	cmpi.b	#9,d3		
	bgt.s	DecStrToLongError

	add.l	d3,d0

DecStrToLongEnd:
	tst.l	d5		;was it negative?
	beq.s	DecStrToLongPositiveEnd	
	neg.l	d0		;negate it

DecStrToLongPositiveEnd:
	movem.l	(sp)+,d2-d5
	bra	AnyStrToLongEnd

DecStrToLongError:
	movem.l	(sp)+,d2-d5
	bra	AnyStrToLongError


	ifne	PEEK
	
**********************************************************************
*
*	AnyLongToStr - Convert 32-bit value to ASCII
*
* Input:  d0 - the value to convert
*         d1 - flags
*         a0 - pointer to a buffer 
* Output: d0 - number of characters written (or null for error)
*         a0 - address of the terminating null
*
*
*	Flags are:
*   LTS_DECIMAL   - normal decimal conversion
*   LTS_SIGN_LONG - decimal signed mode (longword)
*   LTS_SIGN_WORD - signed (word)
*   LTS_SIGN_BYTE - signed (byte)
*   LTS_BINARY    - convert to binary
*   LTS_HEX_UPPER - convert to hex (A-F)
*   LTS_HEX_LOWER - convert to hex (a-f)
*
*
*	You should pass a buffer with at least 11/12 (decimal/negative
* decimal), 9 (hex) or 33 (binary) bytes of space.
*	If flags = #1, checks the 31 bit and if it is high, assumes
* the  value is lower than 0.
*	This string is null-terminated. Address of the terminating
* null is returned in a0. The null is included in number of chars
* written.
*
*	When you specify decimal non-signed mode, then even if
* bit 31 is set you'll get value in range 0 to 4.294.967.295. In
* signed mode you'll get values in range from -2.147.483.647 to
* 2.147.483.648 (negative when bit 31/15/7 is set, depending on the
* choosen mode). Minus sign is written, if needed (and added to chars
* counter returned in d0).
*
*	No prefix is written in hex/bin mode!!! If you need it, write
* it yourself. That's how you can choose between '$'/'0x' :-)
*
*	Alters only scratch registers (d0, d1, a0, a1). Uses 12 bytes
* of stack.
*
*
*	© 1996 by Tadek Knapik (tadek@student.uci.agh.edu.pl).
*		 Public Domain. E-mail appreciated :-)
*
**********************************************************************


LTS_DECIMAL	equ	0
LTS_BINARY		equ	2
LTS_HEX_UPPER	equ	4
LTS_HEX_LOWER	equ	8
LTS_SIGN_LONG	equ	16
LTS_SIGN_WORD	equ	32
LTS_SIGN_BYTE	equ	64
LTS_NEGATIVE	equ	LTS_SIGN_LONG
LTS_HEX		equ	LTS_HEX_UPPER

AnyLongToStr:
	movem.l	d2-d4,-(sp)
	moveq	#0,d2		;the counter

	cmpi.l	#LTS_DECIMAL,d1
	beq.s	LTSDecConvert

	cmpi.l	#LTS_SIGN_LONG,d1
	beq.s	LTSMinDecConvert

	cmpi.l	#LTS_SIGN_WORD,d1
	beq.s	LTSMinWDecConvert

	cmpi.l	#LTS_SIGN_BYTE,d1
	beq.s	LTSMinBDecConvert

	cmpi.l	#LTS_BINARY,d1
	beq	LTSBinConvert

	move.l	#'A',d2		;ASCII base uppercased

	cmpi.l	#LTS_HEX_UPPER,d1
	beq	LTSHexConvert

	move.l	#'a',d2		;ASCII base lowercased

	cmpi.l	#LTS_HEX_LOWER,d1
	beq	LTSHexConvert

LTSError:
	movem.l	(sp)+,d2-d4
	moveq	#0,d1
	moveq	#0,d0
	rts

;--------------------

LTSMinBDecConvert:
	ext.w	d0		;extend to word
LTSMinWDecConvert
	ext.l	d0		;extend to longword

LTSMinDecConvert:
	btst	#31,d0
	beq.s	LTSDecConvert
	move.b	#'-',(a0)+		;the minus sign
	addq.l	#1,d2
	neg.l	d0		;so I need plus now

LTSDecConvert:
	moveq	#0,d1		;zeroes flag
	move.l	#1000000000,d4	;32-bit max power of 10

LTSDecAgain:
	cmp.l	d4,d0
	bcc.s	LTSDecMatch

	tst.l	d1		;write zeroes?
	beq.s	LTSDecNextPass	;not yet

	move.b	#'0',(a0)+		;write it
	addq.l	#1,d2		;one character more
	bra.s	LTSDecNextPass

LTSDecMatch:
	moveq	#1,d1		;means write zeros from now
	moveq	#0,d3		;clear temporary

LTSDecLoop:
	addq.l	#1,d3

	sub.l	d4,d0
	bpl.s	LTSDecLoop		;branch if not less than 0

	add.l	d4,d0		;did it too many times
	subq.l	#1,d3		;as well

	addi.l	#'0',d3		;ASCII 0
	move.b	d3,(a0)+
	addq.l	#1,d2		;the counter

LTSDecNextPass:
	bsr.s	LTSDecDivideByTen
	tst.l	d4
	beq.s	LTSError

	cmpi.l	#1,d4
	bne.s	LTSDecAgain

;less than 10 in d0..

	addi.l	#'0',d0
	move.b	d0,(a0)+
	addq.l	#1,d2

LTSDecOver:
	move.b	#0,(a0)
	suba.l	#1,a0
	addq.l	#1,d2		;null is a character, too
	move.l	d2,d0
	movem.l	(sp)+,d2-d4
	rts


LTSDecDivideByTen:

;
;if your proggy is to be run on 020+ only!
;
;	mulu.l	#10,d4
;	rts

;now 68000 part. Thanks for Simon N Goodwin for this routine!

	moveq	#0,d3		;temporary register
	swap	d4		;higher 16 bits
	move.w	d4,d3	
	divu	#10,d3		;divide higher 16 bits
	swap	d3		;store in high word of d3
	move.w	d3,d4		;safe?
	swap	d4		
	divu	#10,d4                ;divide lower 16 bits
	move.w	d4,d3	
	exg	d3,d4
	rts


;--------------------

; Peephole optimisations and leading zero suppression by SNG

LTSHexConvert:
	tst.l	d0		;;Zero is special
	bne.s	LTSHexSet
	
LTSzero	move.b	#'0',(a0)+
	bra.s	LTSdone

LTSHexSet:	moveq	#8-1,d4		;8 characters

LTSHexLoop:
	rol.l	#4,d0		;prepare next nybble

	moveq	#%1111,d1		;; mask quick load
	and.l	d0,d1		;the nybble
	beq.s	LTSHexTry
	
	bset	#31,d4		;;Flag zero found
	 	
LTSHexTry	tst.l	d4		;; Any zeroes yet?
	bpl.s	LTSHexNxt
	
	moveq	#'0',d3		;ASCII base (for 0-9)

	cmpi.l	#9,d1		;normal or a-f
	ble.s	LTSHexDec

	move.l	d2,d3		;new base (A-F or a-f)
	subi.l	#$A,d1		;new value :-)

LTSHexDec:
	add.l	d1,d3		;add soup base, cover it and
	move.b	d3,(a0)+		;wait for 3 minutes :-)

LTSHexNxt:	dbra	d4,LTSHexLoop

LTSdone:	clr.b	(a0)		;terminate
	suba.l	#1,a0
	movem.l	(sp)+,d2-d4	;give it back..
	moveq	#9,d0		;no way for 8 :-)
	rts

;--------------------


LTSBinConvert:
	moveq	#32-1,d4		;;DBRA 32 characters

	tst.l	d0
	beq.s	LTSzero
	
LTSBinLoop:
	rol.l	#1,d0		;one bit

	moveq	#1,d1		;;spare
	and.l	d0,d1		;;the bit
	beq.s	LTSBinSup
	
	bset	#31,d4		;;Sign
	
LTSBinSup	tst.l	d4
	bpl.s	LTSBinNxt
	

LTSBinOut	add.b	#'0',d1
	move.b	d1,(a0)+		

LTSBinNxt	dbra	d4,LTSBinLoop	;;do it again

	bra.s	LTSdone

;;	clr.b	(a0)		;terminate
;;	subq.l	#1,a0
;;	movem.l	(sp)+,d2-d4	;give it back..
;;	moveq	#33,d0		;am I sure of that?
;;	rts

	endc
	
**********************************************************************

	SECTION	STRINGS,DATA

Version	dc.b	'$VER: POKES 37.6 (28/3/97)',0
DosName	dc.b	'dos'
LibSuffix	dc.b	'.library',0
DevSuffix	dc.b	'.device',0
ResSuffix	dc.b	'.resource',0

Template	dc.b	'W=WORD/S,L=LONG/S,'
Template2	dc.b	'LIB=LIBRARY/K,DEV=DEVICE/K,RES=RESOURCE/K,'
Template3	dc.b	'C=CUSTOM/S,CIAA/S,CIAB/S,AT/S,'

	
	ifeq	PEEK
	
Template4	  dc.b	'ADDRESS/A,VALUE/A',0

	else
	
Template5	  dc.b	'H=HEX/S,BIN=BINARY/S,STRING/S,ADDRESS/A',0
StrFormat	  dc.b	'%s ',10,0

	endc
	

	SECTION	VARIABLES,BSS
	
ExecBase	ds.l	1
DosBase	ds.l	1
CloseBase	ds.l	1		Points at the thing to be closed
ArgTable	ds.l	Address/4+2
ioRequest	ds.l	32		128 bytes should be enough?
OpenName	ds.b	MaxName		Overflows here if necessary

	ifne	PEEK

Buffer	ds.b	36		Not re-entrant	
Result	ds.l	1		Pointer to buffer

	endc

	end
