; AltIOUtila.asm -- assembly-language code for AltIO

;	Last modified March 14, 1981  2:37 PM

.ent MemRead
.ent MemWrite
.ent MemRMW
.ent MemReadBlock32A
.ent MemWriteBlock32A
.ent MemReadBlock40A
.ent MemWriteBlock40A
.ent MemZeroA
.ent MemReadAbsolute
.ent MemWriteAbsolute
.ent MemRMWAbsolute
.ent MemReadRelative
.ent MemWriteRelative
.ent MemRMWRelative
.ent DisplaceMaxcAdr
.ent InputSMI
.ent OutputSMI
.ent OutputSMIPulse
.ent InputSMIErrors
.ent OutputSMITest
.ent ExecuteMicroInstruction
.ent LoadBus
.ent ReadBus
.ent FindZeroMaxcBit
.ent FindOneBit
.ent SetOneMaxcBit
.ent StartIntervalTimer
.ent IncrementMaxcWord
.ent AltIOParityHandler
.ent parityData
.ent AltIOSwatProc
.ent USetBLV
.ent ImpConvTo32
.ent ImpConvTo36
.ent ImpConvFrom32
.ent ImpConvFrom36

.bext adrMTBS
.bext icb


; All the special opcodes defined in the microcode (MaxcAltoCode):

setblv = #63000
mfetch = #66000
mstore = #66001
mrmw = #66002
mblks = #66003
mfblk32 = #66004
msblk32 = #66005
mfblk40 = #66006
msblk40 = #66007
convto32 = #70000
convto36 = #70400
convfrom32 = #71000
convfrom36 = #71400

.srel

MemRead: .MemRead
MemWrite: .MemWrite
MemRMW: .MemRMW
MemReadBlock32A: .MemReadBlock32A
MemWriteBlock32A: .MemWriteBlock32A
MemReadBlock40A: .MemReadBlock40A
MemWriteBlock40A: .MemWriteBlock40A
MemZeroA: .MemZeroA
MemReadAbsolute: .MemReadAbsolute
MemWriteAbsolute: .MemWriteAbsolute
MemRMWAbsolute: .MemRMWAbsolute
MemReadRelative: .MemReadRelative
MemWriteRelative: .MemWriteRelative
MemRMWRelative: .MemRMWRelative
DisplaceMaxcAdr: .DisplaceMaxcAdr
InputSMI: .InputSMI
OutputSMI: .OutputSMI
OutputSMIPulse: .OutputSMIPulse
InputSMIErrors: .InputSMIErrors
OutputSMITest: .OutputSMITest
ExecuteMicroInstruction: .ExecuteMicroInstruction
LoadBus: .LoadBus
ReadBus: .ReadBus
FindZeroMaxcBit: .FindZeroMaxcBit
FindOneBit: .FindOneBit
SetOneMaxcBit: .SetOneMaxcBit
StartIntervalTimer: .StartIntervalTimer
IncrementMaxcWord: .IncrementMaxcWord
AltIOParityHandler: .AltIOParityHandler
parityData: .parityData
AltIOSwatProc: .AltIOSwatProc
USetBLV: .USetBLV
ImpConvTo32: .ImpConvTo32
ImpConvTo36: .ImpConvTo36
ImpConvFrom32: .ImpConvFrom32
ImpConvFrom36: .ImpConvFrom36

.nrel

; Procedures implementing Maxc memory operations

; All take the following arguments:
; maxcAdr
;	pointer to 2-word Maxc address vector.  The top 16 bits
;	are in the first word and the bottom 4 bits left-justified
;	in the second word.
; maxcData
;	pointer to data vector.  For single-word operations, this
;	is 3 words, with the top 32 bits in the first two words and
;	the bottom 8 bits (including the 4 tag bits) in the third.
;	For block operations, this is a vector of either 3-word
;	or 2-word blocks, depending on whether all 40 bits or only
;	the top 32 bits of each Maxc word are being transferred.
; count (block operations only)
;	Maxc word count.

; All return an error code upon completion:
; 0	normal
; 1	parity error detected on data bus during read or RMW
;	(if RMW, the write was nevertheless completed)
; 2	timed out (>80 microseconds to complete operation)
; 4	interface busy when started (should never happen)
; 10	unimplemented operation
; It is conceivable that more than one of the above errors could
; occur at the same time.

; Block operations update the address vector to contain the Maxc
; address of the first word not transferred (this is true whether
; or not the transfer completes successfully).

; Single-word operations
; MemRead(maxcAdr, maxcData)
;	fetches one word from Maxc and puts in data vector.
; MemWrite(maxcAdr, maxcData)
;	stores one word from data vector to Maxc.
; MemRMW(maxcAdr, maxcData)
;	"or"s the data vector with the Maxc word and puts
;	the result both into the data vector and into Maxc.

; Block operations.  These procedures should in general be called
; only via the corresponding procedures in AltIOUtilb.bcpl
; (without the "A" suffix).
; MemZeroA(maxcAdr, nil, count)
;	zeros the specified number of successive Maxc words.
; MemReadBlock32A(maxcAdr, maxcData, count)
;	fetches a block of words from Maxc, putting only
;	the top 32 bits in the data vector.
; MemWriteBlock32A(maxcAdr, maxcData, count)
;	stores a block of words to Maxc, getting only the
;	top 32 bits from the data vector (bottom 8 get
;	garbage).
; MemReadBlock40A(maxcAdr, maxcData, count)
;	fetches a block of words from Maxc, putting all
;	40 bits in the data vector.
; MemWriteBlock40A(maxcAdr, maxcData, count)
;	stores a block of words to Maxc, getting all 40
;	bits from the data vector.

.MemRead:
	mfetch
	jmp mdone

.MemWrite:
	mstore
	jmp mdone

.MemRMW:
	mrmw
	jmp mdone

.MemReadBlock32A:
	sta 3 1 2
	lda 3 3 2		; get count
	mfblk32
	jmp bdone

.MemWriteBlock32A:
	sta 3 1 2
	lda 3 3 2		; get count
	msblk32
	jmp bdone

.MemReadBlock40A:
	sta 3 1 2
	lda 3 3 2		; get count
	mfblk40
	jmp bdone

.MemWriteBlock40A:
	sta 3 1 2
	lda 3 3 2		; get count
	msblk40
	jmp bdone

.MemZeroA:
	sta 3 1 2
	jsr .+4			; make pointer to block of 3 zeroes
	 0
	 0
	 0
	mov 3 1
	lda 3 3 2		; get count
	mblks

bdone:	lda 3 1 2
mdone:	lda 1 c4		; test for "busy when started"
	se 0 1
	 jmp 1 3		; ok, just return
	lda 0 c20000		; reset the memory interface
	sio
	lda 0 c4
	jmp 1 3

c4:	4
c20000:	20000

; Higher-level memory operations
; Coded in assembly language to make them faster

; MemReadAbsolute(adr, maxcData)
; Do a memory read at address "adr", given as a 16-bit number

.MemReadAbsolute:
	sta 3 1 2
	jsr MemOpAbsolute	; common setup code
	jmp .MemRead		; desired operation

; MemWriteAbsolute(adr, maxcData)

.MemWriteAbsolute:
	sta 3 1 2
	jsr MemOpAbsolute	; common setup code
	jmp .MemWrite		; desired operation

; MemRMWAbsolute(adr, maxcData)

.MemRMWAbsolute:
	sta 3 1 2
	jsr MemOpAbsolute	; common setup code
	jmp .MemRMW		; desired operation

; Common setup code for absolute operations
MemOpAbsolute:
	sta 3 3 2		; save return
	sta 1 2 2		; preserve maxcData pointer
	cycle 12.		; low 4 address bits to b0-3
	lda 1 2 2		; recover maxcData pointer
	sta 0 2 2		; store low 4 bits of address
	lda 3 c7777		; mask high 12 bits
	and 3 0
	lda 3 1 2		; recover return address
	sta 0 1 2		; store high 12 bits in frame
	inc 2 0			; use frame+1, +2 as address vector
	jmp @3 2		; return to do the operation


; Unfortunately, the "Relative" operations can't be collapsed into
; common code like the "Absolute" ones because we don't have
; enough temporaries.

; MemReadRelative(displacement, maxcData, base)
; Do a memory read at address "displacement" relative to the
; address vector "base", which defaults to adrMTBS.

.MemReadRelative:
	sta 3 1 2
	sta 1 2 2		; preserve maxcData pointer
	cycle 12.		; low 4 displacement bits to b0-3
	lda 1 c170000		; mask them
	and 0 1
	lda 3 c7777		; mask high 12 bits
	and 3 0
	lda 3 3 2		; get base vector
	sta 0 3 2		; save high 12 displacement bits
	lda 0 @1 2		; get number of arguments
	movzr# 0 0 snc		; skip if odd (3)
	 lda 3 @lvAdrMTBS	; default to MTBS
	lda 0 1 3		; get low 4 bits of base address
	addz 1 0 szc		; add displacement
	 isz 3 2		; carry into high displacement
	lda 1 3 2		; recover high displacement
	lda 3 0 3		; get high 16 bits of base
	add 3 1			; add displacement
	lda 3 1 2		; recover return address
	sta 1 1 2		; store high 16 address bits
	lda 1 2 2		; recover maxcData pointer
	sta 0 2 2		; store low 4 address bits
	inc 2 0			; use frame+1, +2 as address vector
	jmp .MemRead		; go do the operation

; MemWriteRelative(displacement, maxcData, base)

.MemWriteRelative:
	sta 3 1 2
	sta 1 2 2		; preserve maxcData pointer
	cycle 12.		; low 4 displacement bits to b0-3
	lda 1 c170000		; mask them
	and 0 1
	lda 3 c7777		; mask high 12 bits
	and 3 0
	lda 3 3 2		; get base vector
	sta 0 3 2		; save high 12 displacement bits
	lda 0 @1 2		; get number of arguments
	movzr# 0 0 snc		; skip if odd (3)
	 lda 3 @lvAdrMTBS	; default to MTBS
	lda 0 1 3		; get low 4 bits of base address
	addz 1 0 szc		; add displacement
	 isz 3 2		; carry into high displacement
	lda 1 3 2		; recover high displacement
	lda 3 0 3		; get high 16 bits of base
	add 3 1			; add displacement
	lda 3 1 2		; recover return address
	sta 1 1 2		; store high 16 address bits
	lda 1 2 2		; recover maxcData pointer
	sta 0 2 2		; store low 4 address bits
	inc 2 0			; use frame+1, +2 as address vector
	jmp .MemWrite		; go do the operation


; MemRMWRelative(displacement, maxcData, base)

.MemRMWRelative:
	sta 3 1 2
	sta 1 2 2		; preserve maxcData pointer
	cycle 12.		; low 4 displacement bits to b0-3
	lda 1 c170000		; mask them
	and 0 1
	lda 3 c7777		; mask high 12 bits
	and 3 0
	lda 3 3 2		; get base vector
	sta 0 3 2		; save high 12 displacement bits
	lda 0 @1 2		; get number of arguments
	movzr# 0 0 snc		; skip if odd (3)
	 lda 3 @lvAdrMTBS	; default to MTBS
	lda 0 1 3		; get low 4 bits of base address
	addz 1 0 szc		; add displacement
	 isz 3 2		; carry into high displacement
	lda 1 3 2		; recover high displacement
	lda 3 0 3		; get high 16 bits of base
	add 3 1			; add displacement
	lda 3 1 2		; recover return address
	sta 1 1 2		; store high 16 address bits
	lda 1 2 2		; recover maxcData pointer
	sta 0 2 2		; store low 4 address bits
	inc 2 0			; use frame+1, +2 as address vector
	jmp .MemRMW		; go do the operation

; DisplaceMaxcAdr(displacement, maxcAdr, base)
; stores in maxcAdr the result of computing displacement relative
; to base, which defaults to adrMTBS.

.DisplaceMaxcAdr:
	sta 3 1 2
	sta 1 2 2		; save maxcAdr
	cycle 12.		; right-justify high 12 disp bits
	lda 1 c7777		; mask them
	and 0 1
	sta 1 @2 2		; store in maxcAdr!0
	lda 1 c170000		; get low 4 disp bits
	and 0 1
	lda 3 3 2		; get base vector
	lda 0 @1 2		; get number of arguments
	movzr# 0 0 snc		; skip if odd (3)
	 lda 3 @lvAdrMTBS	; default to MTBS
	lda 0 1 3		; get low 4 bits of base address
	lda 3 0 3		; get high displacement
	addz 0 1 szc		; add low base and displacement
	 inc 3 3		; carry into high base
	lda 0 @2 2		; recover high displacement
	add 3 0
	lda 3 2 2		; store result in maxcAdr vector
	sta 0 0 3
	sta 1 1 3
	lda 3 1 2
	jmp 1 3


c170000: 170000
c7777:	7777
lvAdrMTBS: adrMTBS

; Miscellaneous procedures

; FindZeroMaxcBit(maxcWord) = bit number or -1
; Finds the first zero bit in the Maxc word.
; At present, this procedure tests only the first 32 bits.

.FindZeroMaxcBit:
	sta 3 1 2
	mov 0 3
	lda 0 0 3		; get first 16 bits
	com 0 0 szr		; turn zeroes into ones
	 jmp fzmw0		; found one, go search word
	lda 0 1 3		; get second 16 bits
	com 0 0 szr		; turn zeroes into ones
	 jmp fzmw1		; found one, go search word
	mkminusone 0 0		; none found, return -1
	lda 3 1 2
	jmp 1 3

fzmw0:	jsr .FindOneBit		; found zero in first 16 bits
	 1
	lda 3 1 2
	jmp 1 3

fzmw1:	jsr .FindOneBit		; found zero in second 16 bits
	 1
	lda 1 d16
	add 1 0
	lda 3 1 2
	jmp 1 3


; FindOneBit(word) = bit number or -1
; Finds the first one bit in the 16-bit Alto word

.FindOneBit:
	mov 0 1 snr		; quick check for zero
	 jmp retm1
	lda 0 c400		; test for bits on in left half
	subz# 0 1 snc		; skip if ge 400 (unsigned)
	 movs 1 1 skp		; no, look in right byte
	 mkzero 0 0 skp		; left byte, start at zero
	 lda 0 d8		; right byte, start at 8
	movzl 1 1 szc		; test a bit
	 jmp 1 3		; a one, done
	inc 0 0			; increment count, try next
	jmp .-3

retm1:	mkminusone 0 0
	jmp 1 3


d8:	8.
d16:	16.
c400:	400

; SetOneMaxcBit(maxcWord,bit)
; Sets the specified bit number in the Maxc data vector

.SetOneMaxcBit:
	sta 3 1 2
	mov 0 3			; ac3 ← word pointer
	mkzero 0 0		; zero out the data vector
	sta 0 0 3
	sta 0 1 3
	sta 0 2 3
	movzr 1 0		; compute bit number / 16
	movzr 0 0
	movzr 0 0
	movzr 0 0
	add 0 3			; point to correct 16-bit word
	subzr 0 0		; one in bit 0
	neg 1 1			; right cycle (bit number mod 16)
	cycle 0
	sta 0 0 3		; store the resulting bit
	lda 3 1 2
	jmp 1 3



; StartIntervalTimer(interval, chanMask)
; Enables the interval timer to interrupt after interval*.595
; microseconds on the channel(s) specified by chanMask

.StartIntervalTimer:
	sta 1 @itibits		; set channel mask
	sta 0 2 2		; save interval
	rclk			; ac1 ← low part of clock
	lda 0 2 2		; add interval
	add 0 1
	lda 0 c177700		; mask off non-clock stuff
	and 0 1
	sta 1 @ittime		; time at which to interrupt
	mkone 0 0		; enable timer, normal mode
	sit
	jmp 1 3

c177700: 177700
ittime:	525		; page 1 locations
itibits: 423


; IncrementMaxcWord(maxcData, increment)
; Adds the 16-bit "increment" to the 36-bit "maxcData"

.IncrementMaxcWord:
	sta 3 1 2
	mov 0 3			; ac3 ← maxcData pointer
	mov 1 0			; ac0 ← increment
	cycle 12.		; left-cycle increment by 12
	sta 0 2 2		; save result
	lda 1 c170000		; extract former low-order 4 bits
	and 1 0
	lda 1 2 3		; get low 4 bits of maxc word
	addz 0 1 szc		; add increment
	 isz 2 2		; carry into high part of increment
	sta 1 2 3		; store updated low maxc word
	lda 0 2 2		; get back cycled increment
	lda 1 c7777		; extract high 12 bits
	and 1 0
	lda 1 1 3		; get middle 16 bits of maxc word
	addz 0 1 szc		; add increment
	 isz 0 3		; carry into high maxc word
	  nop
	sta 1 1 3		; store updated middle maxc word
	lda 3 1 2
	jmp 1 3

; Procedures for controlling the System Maintenance Interface.
; The procedures on this page may be called from
; non-interrupt level at any time and at interrupt level iff
; Maxc is running (maxcRunning is true).  All others may be called
; only from non-interrupt level and only while Maxc is stopped.

; InputSMI(adr) = value
; Returns the value provided by the SMI input device "adr"

.InputSMI:
	dir
	sta 0 @adreg
	lda 0 @inreg
	jmp smiend


; OutputSMI(adr,value)
; Sends "value" to the SMI output device "adr"

.OutputSMI:
	dir
	sta 0 @adreg
	sta 1 @outreg
	jmp smiend


; OutputSMIPulse(adr,value)
; Same as OutputSMI, but for use when loading memory configuration
; registers only.

.OutputSMIPulse:
	dir
	sta 0 @adreg
	sta 1 @outregp
smiend:	mkzero 1 1
	sta 1 @adreg
	eir
	jmp 1 3


; InputSMIErrors() = value
; Reads and resets the SMI error register.

.InputSMIErrors:
	lda 0 @errinf
	jmp 1 3


; OutputSMITest(value)
; Sets the SMI test register

.OutputSMITest:
	sta 0 @testreg
	jmp 1 3


adreg:	177400
inreg:	177401
outregp: 177402
outreg:	177403
errinf:	177404
testreg: 177405

; Procedures for executing Maxc microinstructions through the SMI.
; Coded in assembly language to make them faster.

; ExecuteMicroInstruction(adr)
; Executes the Maxc microinstruction at adr!0 through adr!3,
; using the control word at adr!4

.ExecuteMicroInstruction:
	sta 3 1 2
	mov 0 3
	dir
	lda 0 smiPIR3		; PIR3-PIR0 are 214-217
	sta 0 @adreg		; load PIR3
	lda 1 3 3
	sta 1 @outreg
	inc 0 0			; load PIR2
	sta 0 @adreg
	lda 1 2 3
	sta 1 @outreg
	inc 0 0			; load PIR1
	sta 0 @adreg
	lda 1 1 3
	sta 1 @outreg
	inc 0 0			; load PIR0
	sta 0 @adreg
	lda 1 0 3
	sta 1 @outreg
	lda 0 smiCR		; load control register
	sta 0 @adreg
	lda 1 4 3
	com 1 1			; must complement data
	sta 1 @outreg
	lda 3 1 2
	jmp smiend

; SMI addresses
smiCR:	210
smiPIR3: 214

; LoadBus(maxcData)
; Loads the Maxc bus register with 36 bits of maxcData

.LoadBus:
	sta 3 1 2
	mov 0 3
	dir
	lda 0 smiBR2		; BR2-BR0 are 211-213
	sta 0 @adreg		; load BR2
	lda 1 2 3
	coms 1 1		; move data to right byte
	sta 1 @outreg
	inc 0 0			; load BR1
	sta 0 @adreg
	lda 1 1 3
	com 1 1			; must complement data
	sta 1 @outreg
	inc 0 0			; load BR0
	sta 0 @adreg
	lda 1 0 3
	com 1 1			; must complement data
	sta 1 @outreg
	lda 3 1 2
	jmp smiend


; ReadBus(maxcData)
; Reads the 36-bit Maxc bus into maxcData

.ReadBus:
	sta 3 1 2
	mov 0 3
	dir
	lda 0 smiB2		; B2-B0 are 201-203
	sta 0 @adreg		; read B2
	lda 1 @inreg
	coms 1 1		; move data to left byte
	sta 1 2 3
	inc 0 0			; read B1
	sta 0 @adreg
	lda 1 @inreg
	com 1 1			; must complement data
	sta 1 1 3
	inc 0 0			; read B0
	sta 0 @adreg
	lda 1 @inreg
	com 1 1			; must complement data
	sta 1 0 3
	lda 3 1 2
	jmp smiend

; SMI addresses
smiBR2:	211
smiB2:	201

; Parity interrupt handler
; Leaves results in parityData vector

.AltIOParityHandler:
	sta 0 save0		; save emulator state
	sta 1 save1
	sta 3 save3
	movr 0 0
	sta 0 saveCarry

	jsr .+1			; save data left by parity task
rregrel: lda 1 offst
	add 3 1			; points to last destination word
	lda 3 cm6		; 6 words to transfer
	lda 0 c613		; starting at 614
	blt

	lda 0 @disp		; save state of display
	sta 0 saveDisp
	mkzero 0 0		; turn it off
	sta 0 @disp

	lda 1 @active		; save active channels
	sta 1 saveActive
	sta 0 @active		; disable all channels

	lda 1 dwait
	lda 0 @disk		; wait for disk and display to stop
	inc 1 1 snr
	mov 0 0 szr
	 jmp .-3

	sta 0 numErrors		; initialize error count
	lda 3 endMem		; highest real memory location +1
	sta 3 .parityData	; set flag

parl1:	lda 0 @ww		; get current waiting wakeups
	movr 0 0		; turn off parity wakeup
	movzl 0 0
	sta 0 @ww
	eir			; reenable copying of NWW to WW
parl2:	mov# 3 3 snr		; see if done
	 jmp parl4		; yes
	lda 0 -1 3		; read a memory word
	neg 3 3			; decrement address (done here to
	com 3 3			; give error time to "take")
	lda 1 @ww		; see if parity interrupt pending
	movr 1 1 snc
	 jmp parl2		; no
	dir			; yes, interrupt system back off
	sta 3 errorAdr		; save address
	sta 0 errorData		; save contents
	sta 0 0 3		; write word back into memory
	isz numErrors		; increment error count
	jmp parl1		; reset WW and keep scanning

; here when done scan
parl4:	dir
	lda 0 saveDisp		; restore display
	sta 0 @disp
	lda 0 saveActive	; restore active interrupts
	sta 0 @active

	lda 0 saveCarry		; restore emulator state
	movl 0 0
	lda 3 save3
	lda 1 save1
	lda 0 save0
	bri			; dismiss interrupt

; AltIOParityHandler (cont'd)

; Data is left in this vector, which must correspond to
; the ParData structure in AltIO.decl

.parityData: 0		; nonzero to report an error
rregs:	.blk 6		; 6 words of data saved by parity task
numErrors: 0		; number of errors detected by sweep
errorAdr: 0		; address of bad word
errorData: 0		; contents of bad word

; other stuff
save0:	0
save1:	0
save3:	0
saveCarry: 0
offst:	rregs+5-rregrel
cm6:	-6
c613:	613
disp:	420
saveDisp: 0
active:	453
saveActive: 0
dwait:	-2000.
disk:	521
endMem: 177000
ww:	452


; Swat context-switching procedure
; Called with 0 when entering Swat and -1 when leaving.

.AltIOSwatProc:
; Cause an interval timer interrupt as we leave Swat, since one probably
; occurred while Swat was running, and Swat may have lost it.
	snz 0 0			; leaving swat?
	 jmp swatp1		; entering, do nothing
	lda 0 @ww		; yes, cause timer interrupt
	lda 1 @imask
	com 1 1
	and 1 0
	adc 1 0
	sta 0 @ww

; Reset the Imp interface and drop host ready
swatp1:	lda 2 @lvICB
	mkone 0 0
	sta 0 0 2		; icMasterReset
	lda 0 c3000		; impControlStatus
	mov 2 1
	sio			; StartIO(impControlStatus, icb)
	lda 0 c7		; icHostReadyOff
	sta 0 0 2
	lda 0 c3000
	sio			; StartIO(impControlStatus, icb)
	jmp 1 3

imask:	423
lvICB:	icb
c3000:	3000
c7:	7


; USetBLV(blv)
; Sets the Boot Locus Vector to blv, in preparation for silent boot.

.USetBLV: setblv
	jmp 1 3

; The following procedures, implemented mostly in microcode,
; convert between the Imp and Maxc2 data formats.
; The Imp format is just a continuous bit stream, packed into 16-bit words.
; The Maxc format is the same continuous bit stream, but packed either
; 32 or 36 bits per Maxc word and stored in Alto memory in the correct
; format for (36-bit) Maxc memory operations.

; All procedures assume that the first 72 bits of a message (Imp and
; Host leader) have been dealt with by the software.  Thus, transfers start
; with the 73rd bit, which happens to fall in the middle of an Alto word
; in Imp format.

; In the descriptions of block formats:
;	00, 01, etc., are names for 4-bit nibbles.
;	** is a nibble whose value is undefined (source) or may be
;	   clobbered arbitrarily (destination).
;	XX is a nibble that must be preserved (destination).
;	-- is a zero nibble.


; ImpConvTo32(dest, source, count)
; Converts Imp message to Maxc 32-bit format.
; It is assumed that the first 8 bits of the source block have
; already been consumed.
; dest = first destination address
; source = first source address
; count = Maxc word count

; Source block format:
;	** ** 00 01	<- dest
;	02 03 04 05
;	06 07 ** **

; Destination block format:
;	00 01 02 03	<- source
;	04 05 06 07
;	** ** ** **

.ImpConvTo32:
	sta 3 1 2
	lda 3 3 2
	convto32
	lda 3 1 2
	jmp 1 3

; ImpConvTo36(dest, source, count)
; Converts Imp message to Maxc 36-bit format.
; It is assumed that the first 8 bits of the source block have
; already been consumed.
; dest = first destination address
; source = first source address
; count = Maxc word count

; Source block format:
;	** ** 00 01	<- dest
;	02 03 04 05
;	06 07 08 09
;	10 11 12 13
;	14 15 16 17
;	18 19 20 21
;	22 23 24 25
;	26 27 28 29
;	30 31 32 33
;	34 35 ** **

; Destination block format:
;	00 01 02 03	<- source
;	04 05 06 07
;	08 ** ** **
;	09 10 11 12
;	13 14 15 16
;	17 ** ** **
;	18 19 20 21
;	22 23 24 25
;	26 ** ** **
;	27 28 29 30
;	31 32 33 34
;	35 ** ** **

.ImpConvTo36:
	sta 3 1 2
	lda 3 3 2
	convto36
	lda 3 1 2
	jmp 1 3


; ImpConvFrom32(dest, source, count)
; Converts Imp message from Maxc 32-bit format.
; It is assumed that the first 8 bits of the destination block have
; already been filled.
; dest = first destination address
; source = first source address
; count = Maxc word count
; Note:  The last 8 bits of the block are not transferred.  The caller
; must specify a Maxc word count one larger than the number of Maxc
; words actually present, and provide 2 words of extra space in the
; destination block.

; Source block format:
;	00 01 02 03	<- dest
;	04 05 06 07
;	** ** ** **

; Destination block format:
;	XX XX 00 01	<- source
;	02 03 04 05
;	06 07 ** **

.ImpConvFrom32:
	sta 3 1 2
	lda 3 3 2
	convfrom32
	lda 3 1 2
	jmp 1 3

; ImpConvFrom36(dest, source, count)
; Converts Imp message from Maxc 36-bit format.
; It is assumed that the first 8 bits of the destination block have
; already been filled.
; dest = first destination address
; source = first source address
; count = Maxc word count
; Note:  The last few bits of the block are not transferred.  The caller
; must specify a Maxc word count one larger than the number of Maxc
; words actually present, and provide 3 words of extra space in the
; destination block.

; Source block format:
;	00 01 02 03	<- dest
;	04 05 06 07
;	08 ** ** **
;	09 10 11 12
;	13 14 15 16
;	17 ** ** **
;	18 19 20 21
;	22 23 24 25
;	26 ** ** **
;	27 28 29 30
;	31 32 33 34
;	35 ** ** **

; Destination block format:
;	XX XX 00 01	<- source
;	02 03 04 05
;	06 07 08 09
;	10 11 12 13
;	14 15 16 17
;	18 19 20 21
;	22 23 24 25
;	26 27 28 29
;	30 31 32 33
;	34 35 ** **

.ImpConvFrom36:
	sta 3 1 2
	lda 3 3 2
	convfrom36
	lda 3 1 2
	jmp 1 3


.end