:TITLE[Jasmine];
%Edit by Fiala 5 October 1981: Parameterize values put in ScanTime and
ScanCBHead base registers; relocate registers for AMesa system; add
WithMidas warning.
Edit by Fiala 6 May 1981: Harmonize with Pilot; eliminate initialization
timer (saved 4 mi); change pixel timer from slot 12b to 13b to avoid
conflict with 10 mb Ethernet timer and fixup JasmineOff for this change;
avoid overwriting TimerTable slots except ones used; JasmineOn must now
have 160000b+JasmineOn as JRam argument.
Edit by Fiala 18 December 1980: Harmonize with Alto microcode; improve bad
timing cases, bum many mi.
Created 19 May 1980 by Joe Maleson
Normally used in conjunction with JasmineHalftone.Mc or JasmineHalftone4.Mc
WARNING: LoadTimer/AddToTimer must be 7 mi 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 7 mi requirement is met. For both Alto and
Cedar, 3 mi from wakeup to AddToTimer and 4 mi after AddToTimer until tasking
are minimum requirements.
WARNING: Printer← must apparently be spaced by two mi (i.e., not in two
consecutive mi).
Three timers are used:
ScanStartTimer:ticks once per scanline, STARTs ScanCBTimer
ScanCBTimer (two-stage):processes a control block, STARTs PixelTimer
PixelTimer:reads in bytes, STARTs ScanCBTimer
Timer dispatch entries replace entries in Timer.Mc
slot 17b (At[TimerTable,0]) = refresh
slot 16b (At[TimerTable,1]) = slow stage of ScanStartTimer here
slot 15b = fast stage of ScanStartTimer here
slot 14b (At[TimerTable,3]) = ScanCBTimer here
slot 13b (At[TimerTable,4]) = PixelTimer here
slot 12b (At[TimerTable,5]) = 10 mb ethernet
slot 06b (At[TimerTable,11]) = 3 mb ethernet
slot 04b (At[TimerTable,13]) = 2nd ethernet (3 mb or 10 mb)
slot 02b (At[TimerTable,15]) = audio
PROBLEMS:
1) ScanTimeLoc and ScanCBHeadLoc should be made adjacent to eliminate one
base register and save 3 mi; these locations need to be specified for Cedar,
probably at 177400 + 20*16.
2) Save the final NOP in MRTloop by advancing the Printer← stuff.
3) Change the "Alto ticks" stuff which must be converted for LoadTimer
to appropriate Dolphin units.
4) ScanBufferHi is not initialized.
5) Quadalign the buffer headers; possibly eliminate the pointer to the
buffer by uniting the buffer header and the buffer; possibly arrange the
store count and status to be in words 0 and 1 of the header to allow a
PStore2 in haveCurrent.
6) Find out why ScanCBHead has to be updated for each buffer.
7) Why is jStoreCount stored at jScanBuffer+1 in haveCurrent?
%
IFE[WithMidas,1,ER[WARNING.Jasmine.can’t.be.used.with.Midas]];
SetTask[TTask];
:IF[AltoMode]; ********************************
*These registers are targets for PFetch/PStore, so they must be RM 340-357.
RV[jTimerData,43];*general temporary
RV[jScanWait,44];*temporary used at top of ScanStartTimer
RV[jScanData,44];*used in PixelTimer
RV[jStoreCount,45];
RV[jPrinterControl,46];*used in ScanStartTimer, PixelTimer
RV[jStatusReg,47];*used in ScanCBTimer
RV2[jScanBuffer,jScanBufferHi,50];
*These registers may be RM 320-377b.
RV2[jScanTime,jScanTimeHi,30];*contains wait count in ticks
Set[ScanTimeLoc,526];
RV2[jScanCBHead,jScanCBHeadHi,32];*ScanCBHeadLoc+1 is StartCommand
Set[ScanCBHeadLoc,736];
RV[jStepperState,34];
RV[jPixelTimerSet,35];*used in PixelTimer, ScanCBTimer
:ELSE; ****************************************
Set[tRB,LShift[And[TTask,3],4]];
*These registers are targets for PFetch/PStore, so they must be RM 340-357.
RV[jScanWait,Add[tRB,0]];*temporary used at top of ScanStartTimer
RV[jScanData,Add[tRB,0]];*used in PixelTimer
RV[jTimerData,Add[tRB,1]];*general temporary
RV[jPrinterControl,Add[tRB,2]];*used in ScanStartTimer,PixelTimer
RV[jStatusReg,Add[tRB,3]];*used in ScanCBTimer
RV2[jScanBuffer,jScanBufferHi,Add[tRB,4]];
RV[jStoreCount,Add[tRB,6]];
*These registers may be in RM 320-377b.
RV2[jScanTime,jScanTimeHi,34];*contains wait count in ticks
Set[ScanTimeLoc,526];
RV2[jScanCBHead,jScanCBHeadHi,36];*ScanCBHeadLoc+1 is StartCommand
Set[ScanCBHeadLoc,736];
RV[jStepperState,Add[tRB,7]];
RV[jPixelTimerSet,Add[tRB,10]];*used in PixelTimer, ScanCBTimer
:ENDIF; ***************************************
*ScanStartTimer (slot 16b is slow stage; slot 15b is fast stage).
PFetch1[jScanTime,jScanWait,0], At[TimerTable,1];
LoadPage[JasPage];**Cannot have LoadPage in previous mi
jTimerData ← HiA[50054];
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
*64 cycles from Timer wakeup to return
*load ScanCBTimer with state: 5, data: 2, slot: 14B
*SCanCBTimer is a simple timer; load it with state 5, data 2, slot 14b.
jTimerData ← (jTimerData) + (LoA[50054]);
LoadTimer[jTimerData];
T ← jScanWait;
jScanWait ← (LSh[jScanWait,1]) + T;*3*jScanWait
T ← RSh[jScanWait,6];
jScanWait ← (LSh[jScanWait,1]) - T;*6*jScanWait - 3*jScanWait/64
*Slots 15b and 16b are two-stage timer.
*Load slow timer with high 7 bits
T ← 160400C;*(slot=16B,state=1) RCy 4
T ← (LdF[jScanWait,1,7]) + T;
jTimerData ← T;
jTimerData ← LCy[jTimerData,4];*get into appropriate fields
LoadTimer[jTimerData];
PFetch1[jScanCBHead,jPrinterControl,1];*read PrinterControl;
*Load fast timer with low 8 bits
T ← 154400C;*(slot=15B,state=11B) RCy 4
T ← (LdF[jScanWait,10,10]) + T;
jTimerData ← T;
jTimerData ← LCy[jTimerData,4];*get into appropriate fields
jPrinterControl ← (jPrinterControl) xor (177C);*invert command bits
*12 cycles between PFetch1 and previous mi ensures 14 cycles between
*previous LoadTimer and this AddToTimer.
AddToTimer[jTimerData];
*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 ← jPrinterControl;*<0><START><SkipCount>
jPrinterControl ← (jPrinterControl) xor (1000C);
*<1><START><SkipCount>
jPrinterControl ← T, Printer ← jPrinterControl, NoRegILockOK;
Nop;
PrgPrC:Printer ← jPrinterControl, Return;*<0><START><SkipCount>
%ScanCBTimer (slot 14b) starts at haveCurrent or headCheck
started by ScanStartTimer
started by PixelTimer when jStoreCount goes to 0
There is a current scan buffer:
assign status and dequeue
if DataLate, clear CB queue
%
jStatusReg ← (Zero) - 1, At[TimerTable,3];
**Cannot have LoadPage in previous mi since only 2nd after wakeup and might
**page fault.
LU ← jScanBuffer, LoadPage[JasPage];
PFetch1[jScanCBHead,jScanBuffer,0], DblGoToP[haveCurrent,headCheck,ALU#0];
*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
OnPage[JasPage];
haveCurrent:
LU ← jStoreCount;
LU ← jScanBuffer, GoTo[pixelTimerEntry,ALU=0];*interlock
PStore1[jScanBuffer,jStoreCount,1];
jStatusReg ← (jStatusReg) - 1;*StatusDATALATE;
*Smash PixelTimer, so it won’t run, to state 4 (idle), data 0, slot 13b
jPixelTimerSet ← HiA[40013];
jPixelTimerSet ← (jPixelTimerSet) + (LoA[40013]);
LoadTimer[jPixelTimerSet];
PStore1[jScanBuffer,jStatusReg,2];
jScanBuffer ← 0C;
jStoreCount ← 0C;
PStore1[jScanCBHead,jScanBuffer,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[jScanBuffer,jStatusReg,2];*jStatusReg = StatusDONE
PFetch1[jScanBuffer,jScanBuffer,0];
PStore1[jScanCBHead,jScanBuffer,0], GoTo[headCheck];
*Check for input buffer (19 cycles to haveHead, 20 to noHead return).
*ScanCB structure:
*link
*command (Read,Delay,Forward,Back)
*status/count (negative = status, pos=count)
*buffer
headCheck:
LU ← jScanBuffer;
jStoreCount ← 0C, Skip[ALU#0];
Return;*no head
*64 cycles to here via haveCurrent; 23 cycles to here via headCheck.
*38 cycles here to return on READ, 68 or 70 on BACK/FORWARD, and 58 on DELAY
PFetch1[jScanBuffer,jTimerData,1];
LU ← Dispatch[jTimerData,16,2];
LU ← Dispatch[jStepperState,16,2], Disp[.+1];
jPixelTimerSet ← HiA[50053], GoTo[cREAD], DispTable[4];*READ
*take CB off queue, mark DONE
*DELAY: wait until next start pulse (just throw command away)
cDELAY:PStore1[jScanBuffer,jStatusReg,2], GoTo[cDELAY1];*DELAY
jStepperState ← 55C, Disp[stepDone];*FORWARD
jStepperState ← 56C, Disp[stepDone];*BACK
*Load PixelTimer with state 5, data 2, slot 13b
cREAD:jPixelTimerSet ← (jPixelTimerSet) + (LoA[50053]);
LoadTimer[jPixelTimerSet];
PFetch1[jScanBuffer,jStoreCount,2];
PFetch1[jScanBuffer,jScanBuffer,3];
jPrinterControl ← 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 ← jStepperState, GoTo[stepDone1], DispTable[4];
jStepperState ← (jStepperState) xor (2C), GoTo[stepDone];
jStepperState ← (jStepperState) xor (1C), GoTo[stepDone];
jStepperState ← (jStepperState) xor (3C), GoTo[stepDone];
stepDone1:
jStepperState ← (jStepperState) xor (1000C);*<1><MOTORCTL><StepState>
jStepperState ← T, Printer ← jStepperState, NoRegILockOK;
Nop;
Printer ← jStepperState, GoTo[cDELAY];*<0><MOTORCTL><StepState>
cDELAY1:
PFetch1[jScanBuffer,jTimerData,0];*link
jScanBuffer ← 0C;
PStore1[jScanCBHead,jTimerData,0], Return;
%PixelTimer code (slot 13b): 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
%
Nop, At[TimerTable,4];
*These two mi duplicate those at done1; cannot have LoadPage in previous mi.
LU ← (jStoreCount) - 1, LoadPage[JasPage];
T ← Printer, DblGoToP[endCount,nextByte,ALU<0];
OnPage[JasPage];
endCount:
jStatusReg ← (Zero) - 1, GoTo[haveCurrent];
nextByte:
jPrinterControl ← 100400C, Skip[ALU<0];
done1: AddToTimer[jPixelTimerSet], GoTo[NNNopReturn];
Printer ← jPrinterControl;
jPrinterControl ← 400C;
Printer ← jPrinterControl;
jStoreCount ← (jStoreCount) - 1, GoTo[oddByte,R Odd];
jScanData ← T, Skip[ALU#0];
jScanData ← LSh[jScanData,10], GoTo[done1];
jScanData ← LSh[jScanData,10];
LU ← (jStoreCount) - 1;
T ← Printer, DblGoTo[endCount,nextByte,ALU<0];
oddByte:
AddToTimer[jPixelTimerSet];
jTimerData ← 377C;
T ← (jTimerData) and T;
LU ← jScanBuffer;
jScanData ← (jScanData) + T, Skip[ALU#0];
Return;
jScanData ← (jScanData) xnor (0C);
PStore1[jScanBuffer,jScanData,0];
jScanBuffer ← (jScanBuffer) + 1, Return;
*Initialization: enter here with Mesa JRam 160000b+JasimineOn
jTimerData ← HiA[50056], At[JasmineOn];*State 5, data 2, slot 16b
jTimerData ← (jTimerData) + (LoA[50056]);
LoadTimer[jTimerData];
jScanTimeHi ← 0C;*Only word 0 used at ScanTimeLoc
jScanTime ← HiA[ScanTimeLoc];
jScanTime ← (jScanTime) + (LoA[ScanTimeLoc]);
jScanCBHeadHi ← 0C;*Two words used at ScanCBHeadLoc
jScanCBHead ← HiA[ScanCBHeadLoc];
jScanCBHead ← (jScanCBHead) + (LoA[ScanCBHeadLoc]);
jScanBuffer ← 0C;
jStoreCount ← 0C, Return;
SetTask[0];*EMULATOR LEVEL STUFF (can’t use Timer registers!)
NNNopReturn:
Nop;
NNopReturn:
Nop;
NopReturn:
Nop;
Retn:Return;
*Shut down timers 13b to 16b
xBuf ← LoA[40013], At[JasmineOff];
xBuf1 ← 2C;
JasmineOff2:
xBuf ← (xBuf) or (HiA[40013]);
LoadTimer[xBuf];
xBuf1 ← (xBuf1) - 1, Skip[R>=0];
GoTo[NNopReturn];
Nop;
Nop;
Nop;
Nop;
xBuf ← (xBuf) + 1, GoTo[JasmineOff2];
Stack ← (Stack) xor (177C), At[JasminePulse];
Printer ← Stack;
Stack ← (Stack) xor (1000C);
Printer ← Stack;
Stack ← (Stack) xor (1000C);
Printer ← Stack&-1, Return;
:END[Jasmine];