:TITLE[Jasmine];
%Last edited: 18 December 1980 by Fiala; 19 May 1980 by Maleson previously

Normally used in conjunction with JasmineHalftone.Mc or JasmineHalftone4.Mc

WARNING: LoadTimer/AddToTimer must be 14 cycles apart and an mi containing
one of these must not have a branch condition. The storage refresh code in
Timer.Mc controls the way the 14 cycle requirement is met. It has 6 cycles
from its wakeup to its AddToTimer and 8 cycles after the AddToTimer until
tasking--6 and 8 cycles are the minimums to meet here as well.

NOTE: labels are named to match Alto microcode labels

Three timers are used:
ScanStartTimer:
ticks once per scanline, STARTs ScanCBTimer
ScanCBTimer:
processes a control block, STARTs PixelTimer
PixelTimer:
reads in bytes, STARTs ScanCBTimer
%
SetTask[TTask];

*RM 343-56, 367-70 were available on 3 Dec 1980
RV2[ScanTime,ScanTimeHi,44];
*526 (contains wait count in ticks)
RV2[ScanCBHead,ScanCBHeadHi,46];
*736; and 737 is StartCommand

RV[TimerData,51];
*general temporary
RV[PrinterControl,52];
*used in ScanStartTimer,PixelTimer

RV[ScanWait,50];
*temporary used at top of ScanStartTimer
RV[StatusReg,53];
*used in ScanCBTimer
RV[ScanData,50];
*used in PixelTimer

RV2[ScanBuffer,ScanBufferHi,54];
RV[StoreCount,56];
RV[StepperState,70];
RV[PixelTimerSet,67];
*used in PixelTimer, ScanCBTimer

OnPage[JasPage];

*1 alto tick = 38.08 usec, 1 D0 tick = 6.4 usec
*approx conversion: D0Ticks = 6*AltoTicks - (3*AltoTicks)/64
*timer format: state: 0-3, data: 4-11, slot: 12-15
MRTloop:
*58 cycles from Timer wakeup to return
*load up ScanCBTimer with state: 5, data: 2, slot: 14B
TimerData ← 50000C;*state=5, highdata=0
TimerData ← (TimerData) + (54C);*lowdata = 2, slot=14B
LoadTimer[TimerData];
T ← ScanWait;
ScanWait ← (LSh[ScanWait,1]) + T;*3*ScanWait
T ← RSh[ScanWait,6];
ScanWait ← (LSh[ScanWait,1]) - T;*6*ScanWait - 3*ScanWait/64
*load slow timer with high 7 bits
T ← 160400C;*slot=16B,state=1
T ← (LdF[ScanWait,1,7]) + T;
TimerData ← T;
TimerData ← LCy[TimerData,4];*get into appropriate fields
LoadTimer[TimerData];
PFetch1[ScanCBHead,PrinterControl,1];*read PrinterControl;
*load fast timer with low 8 bits
T ← 154400C;*slot=15B,state=11B
T ← (LdF[ScanWait,10,10]) + T;
TimerData ← T;
TimerData ← LCy[TimerData,4];*get into appropriate fields
PrinterControl ← (PrinterControl) xor (177C);*invert command bits
*12 cycles between PFetch1 and previous mi ensures 14 cycles between
*previous LoadTimer and this AddToTimer.
AddToTimer[TimerData];
*command output format:
* 0:5 don’t care, 6:6 command enable, 7:7 0, 8:8 don’t care, 9:15 command.
*Pulse start and finally TASK.
T ← Printer ← PrinterControl;*<0><START><SkipCount>
PrinterControl ← (PrinterControl) xor (1000C);
*<1><START><SkipCount>
PrinterControl ← T, Printer ← PrinterControl, NoRegILockOK;
Nop;**Apparently spacing Printer← by two mi is necessary
PrgPrC:
Printer ← PrinterControl, Return;*<0><START><SkipCount>


*ScanCBTimer starts at haveCurrent or noCurrent
* started by ScanStartTimer
* started by PixelTimer when StoreCount goes to 0
*There is a current scan buffer:
*
assign status and dequeue
*
if DataLate, clear CB queue
haveCurrent:
*73 cycles from wakeup to return unless PixelTimerEntry jump
*27 cycles from wakeup to already-idle return
*74 cycles to noHead return
*102 to 134 cycles to headCheck return
PFetch1[ScanCBHead,ScanBuffer,0];
LU ← StoreCount;
LU ← ScanBuffer, Goto[pixelTimerEntry,ALU=0];*interlock
PStore1[ScanBuffer,StoreCount,1];
StatusReg ← (StatusReg) - 1;*StatusDATALATE;
*smash PixelTimer, so it won’t run
PixelTimerSet ← 40000C;*state=4(idle), highdata=0
PixelTimerSet ← (PixelTimerSet) + (12C);*lowdata = 0, slot=12B
LoadTimer[PixelTimerSet];
PStore1[ScanBuffer,StatusReg,2];
ScanBuffer ← 0C;
StoreCount ← 0C;
PStore1[ScanCBHead,ScanBuffer,0], Return;

*no error: pixel task has completed
*if emulator has stored 0 into ScanCBHead, just return
*21 cycles from wakeup to here
pixelTimerEntry:
Goto[NopReturn,ALU=0];*already set to idle
PStore1[ScanBuffer,StatusReg,2];*StatusReg = StatusDONE
PFetch1[ScanBuffer,ScanBuffer,0];
PStore1[ScanCBHead,ScanBuffer,0], Goto[headCheck];

*Check for input buffer (19 cycles to haveHead, 20 to noHead return).
noCurrent:
PFetch1[ScanCBHead,ScanBuffer,0], Goto[headCheck];

*ScanCB structure:
*
link
*
command (Read,Delay,Forward,Back)
*
status/count (negative = status, pos=count)
*
buffer
headCheck:
LU ← ScanBuffer;
StoreCount ← 0C, Skip[ALU#0];
Return;*no head
*64 cycles to here via haveCurrent; 23 cycles to here via noCurrent.
*38 cycles here to return on READ, 68 or 70 on BACK/FORWARD, and 58 on DELAY
PFetch1[ScanBuffer,TimerData,1];
LU ← Dispatch[TimerData,16,2];
LU ← Dispatch[StepperState,16,2], Disp[.+1];
PixelTimerSet ← 50000C, Goto[cREAD], DispTable[4];*READ
*take CB off queue, mark DONE
*DELAY: wait until next start pulse (just throw command away)
cDELAY:
PStore1[ScanBuffer,StatusReg,2], Goto[cDELAY1];*DELAY
StepperState ← 55C, Disp[stepDone];*FORWARD
StepperState ← 56C, Disp[stepDone];*BACK

*Load PixelTimer with state: 5, data: 2, slot: 12B
cREAD:
PixelTimerSet ← (PixelTimerSet) + (52C);
LoadTimer[PixelTimerSet];
PFetch1[ScanBuffer,StoreCount,2];
PFetch1[ScanBuffer,ScanBuffer,3];
PrinterControl ← 400C, Goto[PrgPrC];*enable input

*MOTORCTL=5
*<000000><ENABLE><00><MOTORCTL><xxxx>

*0101xxxx = disabled = 120
*BUT: we need to invert the low order 7 bits (ala Alto)

*FIFOShft=0, Alto8=0, EnableInput=0
*For FORWARD: inverted sequence is: 0,1,3,2 (55,57,54,56)
*For BACK: inverted sequence is: 2,3,1,0 (56,54,57,55)
stepDone:
*<0><MOTORCTL><StepState>
T ← Printer ← StepperState, Goto[stepDone1], DispTable[4];
StepperState ← (StepperState) xor (2C), Goto[stepDone];
StepperState ← (StepperState) xor (1C), Goto[stepDone];
StepperState ← (StepperState) xor (3C), Goto[stepDone];

stepDone1:
StepperState ← (StepperState) xor (1000C);*<1><MOTORCTL><StepState>
StepperState ← T, Printer ← StepperState, NoRegILockOK;
Nop;**Apparently spacing Printer← by two mi is necessary
Printer ← StepperState, Goto[cDELAY];*<0><MOTORCTL><StepState>

cDELAY1:
PFetch1[ScanBuffer,TimerData,0];*link
ScanBuffer ← 0C;
PStore1[ScanCBHead,TimerData,0], Return;

JasmineInit:
*set up ScanStartTimer to run
TimerData ← (TimerData) + (56C); *lowdata=2,slot=16
LoadTimer[TimerData];
*first time initialization (min of 8 cycles)
ScanTimeHi ← 0C;
ScanTime ← 400C;
ScanTime ← (ScanTime) + (126C);
ScanCBHeadHi ← 0C;
ScanCBHead ← 400C;
ScanCBHead ← (ScanCBHead) + (336C);
ScanBuffer ← 0C;
StoreCount ← 0C, Return;


%PixelTimer code: between shift out pulse and data read, leave a minimum of
850 nsecs. Printer input format is normally:
0:0 FIFOShift=0, 1:5 unused, 6:6 Alto8=0, 7:7 InputEnable=1, 8:15 FIFOData
shift out by dropping FIFOShift to 0, and then back high
%
endCount:
StatusReg ← (Zero) - 1, Goto[haveCurrent];
nextByte:
PrinterControl ← 100400C, Skip[ALU<0];
done1:
AddToTimer[PixelTimerSet], Goto[NNNopReturn];
Printer ← PrinterControl;
PrinterControl ← 400C;
Printer ← PrinterControl;
StoreCount ← (StoreCount) - 1, Goto[oddByte,R Odd];
ScanData ← T, Skip[ALU#0];
ScanData ← LSh[ScanData,10], Goto[done1];
ScanData ← LSh[ScanData,10];
LU ← (StoreCount) - 1;
T ← Printer, DblGoto[endCount,nextByte,ALU<0];
oddByte:
AddToTimer[PixelTimerSet];
TimerData ← 377C;
T ← (TimerData) and T;
LU ← ScanBuffer;
ScanData ← (ScanData) + T, Skip[ALU#0];
Return;
ScanData ← (ScanData) xnor (0C);
PStore1[ScanBuffer,ScanData,0];
ScanBuffer ← (ScanBuffer) + 1, Return;

*catching the wakeup
*slot 17 (At[x,0]) = refresh
*slot 6 (At[x,11]) = ether
*Slot 16 (TimerTable+1, ScanStartTimer), 14 (TimerTable+3, ScanCBTimer),
*12 (TimerTable+7, PixelTimer), 10 (TimerTable+7, InitTimer)
*are used below; TimerTable 20 and 21 and 6 (slot 11) are also used.
LoadPage[JasPage], At[TimerTable,1];
PFetch1[ScanTime,ScanWait,0], GotoP[MRTloop], At[TimerTable,20];

LU ← ScanBuffer, LoadPage[JasPage], At[TimerTable,3];
StatusReg ← (Zero) - 1, DblGotoP[haveCurrent,noCurrent,ALU#0], At[TimerTable,21];

LU ← (StoreCount) - 1, LoadPage[JasPage], At[TimerTable,5];
T ← Printer, DblGotoP[endCount,nextByte,ALU<0], At[TimerTable,6];

LoadPage[JasPage], At[TimerTable,7];
TimerData ← 50000C, GotoP[JasmineInit], At[TimerTable,10];*state=5, highdata=0

SetTask[0];
*EMULATOR LEVEL STUFF (can’t use Timer registers!)

*initialization: start up InitTimer, state: 5,data: 2, slot: 10B;
xBuf ← 50000C, At[JasmineOn];
xBuf ← (xBuf) + (50C);
LoadTimer[xBuf];
NNNopReturn:
Nop;
NNopReturn:
Nop;
NopReturn:
Goto[Retn];

*shut down all timers
xBuf ← 12C, At[JasmineOff];
JasmineOff2:
Dispatch[xBuf,13,1];
xBuf ← (xBuf) or (40000C), Disp[.+1];
LoadTimer[xBuf], Skip, DispTable[2];
Retn:
Return;
Nop;
Nop;
Nop;
Nop;
xBuf ← (xBuf) + (2C), Goto[JasmineOff2];


Stack ← (Stack) xor (177C), At[JasminePulse];
**Apparently it is necessary to space Printer← by two cycles.**
Printer ← Stack;
Stack ← (Stack) xor (1000C);
Printer ← Stack;
Stack ← (Stack) xor (1000C);
Printer ← Stack&-1, Return;

:END;