File: clock.asm

package info (click to toggle)
xdaliclock 2.48-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 17,328 kB
  • sloc: ansic: 8,377; sh: 3,522; objc: 2,029; makefile: 1,220; java: 1,044; javascript: 802; asm: 419; xml: 337; perl: 269; ruby: 5
file content (613 lines) | stat: -rw-r--r-- 14,672 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613

; This is Steve Capp's clock program, written in the early days of Macintosh.

; The original program had every kind of display dependency: base address,
; rowbytes, and bounds.  As an added bonus, the code segment was self-
; modifying.  (The _code_ wasn't self-modifying, but the code segment had
; an embedded variable area.)  I disassembled it with MacNosy, fixed all(?)
; the compatibility bugs, threw in a few optimizations, and here it is.
; Somewhere along the way, I lost most of it: the code size (bytes in the
; CODE 1 resource) dropped from 3478 bytes to less than 1100.

; I've cleared out most of the "magic numbers" from the code, but I still
; don't understand how the drawing works.  Good luck.

; Tested on a Mac Plus and a MegaScreen.

; Ephraim Vishniac
; February 22, 1987
;     uucp:	decvax!wanginst!wang!ephraim
; MCI mail:	masstech
; US snail:	P.O. Box 1357
;		East Arlington, MA 02174

; Miscellaneous things I discovered:
;	When the mouse is below row 280 on the screen, an informative
;	message is displayed.  In this version, the string displayed
;	depends on the the mouse column (horizontal coordinate) reduced
;	modulo the number of strings.
;
;	Pressing the mouse while the cursor is above row 280 causes the
;	date to be displayed instead of the time.  Pressing the mouse
;	while the cursor is below row 280 exits to the shell.
;
;	To prevent screen burn-in, the digits move down and up through
;	a range of 75 rows.  The rate of motion is one row every sixteen
;	seconds.

	Include Traps.D		; Use System and ToolBox traps
	Include SysEquX.D	; Use System equates
	Include	ToolEqu.D	; Use ToolBox equates
	Include	QuickEquX.D	; Use QuickDraw equates

;	Some equates

qdGlobals	equ	-4		; qdGlobals are at -4(A5)

NumNum		equ	6		; six numerals
NumWidth	equ	8		; numeral is eight bytes wide (64 pixels)
DispWidth	equ	NumNum*NumWidth	; width of display area (bytes)
DispRows	equ	72		; display is 72 rows high
DispBytes	equ	DispWidth*DispRows
Steps		equ	9		; Nine steps in transformation
WorkBytes	equ	DispBytes*Steps	; size of work area 

StringBase	equ	1000		; first 'STR ' resource
NumStrings	equ	4		; number of 'STR ' resources

StartRow	equ	60		; initial row for clock
CursorBound	equ	280		; cursor transition is at row 280
MessageRow	equ	CursorBound+30	; Starting row for announcements

;	Globals


MouseRow	ds.w	1		; MousePoint.v
MouseCol	ds.w	1		; MousePoint.h
ShowDate	ds.b	1		; 0 = Show Time, 1 = Show Date
LowCursor	ds.b	1		; cursor is low on screen

Year		ds.w	1		; This is a date-time record
Month		ds.w	1		; 
Day		ds.w	1		; 
Hour		ds.w	1		; 
Minute		ds.w	1		; 
Second		ds.w	1		; 
WeekDay		ds.w	1		; (not used by us, but set by _Secs2Date)

MyPort		ds.b	portRec		; our GrafPort record
MyBounds	equ	MyPort+portBits+bounds
MyTopLeft	equ	MyBounds+topLeft
MyTop		equ	MyTopLeft+v
MyLeft		equ	MyTopLeft+h
MyBotRight	equ	MyBounds+botRight
MyBottom	equ	MyBotRight+v
MyRight		equ	MyBotRight+h
MyBaseAddr	equ	MyPort+portBits+baseAddr
MyRowBytes	equ	MyPort+portBits+rowBytes

TrendDown	ds.b	1		; 1 = Down, 0 = Up

PaintPtr	ds.l	1		; screen address to start painting

OldData		ds.l	NumNum		; previous date or time, expanded
NewData		ds.l	NumNum		; new date or time, expanded

WorkArea	ds.l	1		; pointer to work area

glob39		ds.l	NumNum		; pointers to mask resource (RAWB 20)

glob46		ds.l	10		; pointers to numeral data (RAWB 1-10)

MyTempRect	ds.w	4		; rectangle used in CHECKCUR

;	Start here!

MAIN
	JSR	INIT

	; Calculate starting point for painting the screen
	MOVEQ	#StartRow,D0		; rows down from top
	mulu	MyRowBytes(A5),D0	; times bytes-per-row
	ADD.L	MyBaseAddr(A5),D0	; plus start of bitmap
	move.w	MyRight(A5),D1		; D1.W = right bound
	sub.w	MyLeft(A5),D1		; D1.W = screen width
	sub.w	#DispWidth*8,D1		; width of our drawing (pixels)
	lsr.w	#4,D1			; divide by two to center,
	ext.l	D1			;   and by eight for bytes
	add.l	D1,D0			; center the clock
	MOVE.L	D0,PaintPtr(A5)

	; Blackout!
	_HideCursor			; put the cursor away

	; blacken the entire screen
	PEA	MyBounds(A5)		; rectangle for screen
	PEA	qdGlobals+black(a5)	; fill with black
	_FillRect

; One-time setup for main loop

	JSR	GetTime

	JSR	SetData

	Moveq	#NumNum-1,D0		; copy NewData to OldData
	LEA	NewData(A5),A0
	LEA	OldData(A5),A1
@1	MOVE.L	(A0)+,(A1)+
	dbra	D0,@1


	CLR.B	TrendDown(A5)		; establish a trend
	BRA	@2			; hop into loop

; This is the top of the main loop

@3	move.L	Time,D6			; D6 = old time
	JSR	MELT

	; Wait for the second to change
@4	cmp.L	Time,D6			; compare old time to current time
	beq	@4			; loop until the seconds change
	JSR	GetTime			; and then get the full time

	JSR	SetData			; convert time/date to data

	; The following stuff moves the image up or down by one row
	; every sixteen seconds to avoid screen burn-in.

@6	MOVE.B	Time+3,D0		; Get low order byte of time
	and.b	#$F,D0			; every 16 secs, shift
	BNE	@9			; branch if non-zero, don't shift

	MOVE.W	MyRowBytes(A5),D0
	EXT.L	D0			; D0.L = bytes per row
	TST.B	TrendDown(A5)		; Should we move up or down?
	BEQ	@7			; branch if up
	ADD.L	D0,PaintPtr(A5)		; move down one row
	BRA	@8
@7	SUB.L	D0,PaintPtr(A5)		; move up one row

@8	DBRA	D7,@9			; every once in a while, switch
@2	MOVE.W	#DispRows+2,D7		; recharge the trend counter
	BCHG.B	#0,TrendDown(A5)	; flip the trend flag

@9	JSR	CHECKCUR		; check the mouse position

	PEA	glob46(A5)		; pointers to numeral data
	PEA	OldData(A5)		; old time or date
	PEA	NewData(A5)		; new time or date
	Move.L	WorkArea(A5),-(A7)	; pointer to work area
	PEA	glob39(A5)		; offsets into mask resource
	JSR	PROC30			; build new images

	; If Button, Show Date
	; If Not Button, Show Time
	; If LowCursor & Button, Fall out
	CLR	-(A7)
	_Button				; (A7) = Button
	MOVE.B	(A7),ShowDate(A5)	; If button, Show Date

	MOVE.B	LowCursor(A5),D0
	AND.B	(A7)+,D0		; D0 = LowCursor & Button
	BEQ	@3

	RTS				; Exit to wherever we came from...


StringPtr	set	8		; single parameter for ANNOUNCE

;-refs - CHECKCUR	

ANNOUNCE
	LINK	A6,#-0

	move.w	#SrcBic,-(A7)
	_TextMode

	CLR	-(A7)			; for string width
	MOVE.L StringPtr(A6),-(A7)	; string pointer
	_StringWidth			; (A7) = string width

	move.w	MyRight(A5),D1		; D1.W = right bound
	sub.w	MyLeft(A5),D1		; D1.W = screen width
	SUB.W	(A7)+,D1		; D1.W = excess screen width
	LSR.W	#1,D1			; D1 = half of excess screen width
	move.w	D1,-(A7)
	move.w	#MessageRow,-(A7)	; row for drawing text
	_MoveTo

	MOVE.L StringPtr(A6),-(A7)	; string pointer
	_DrawString

	UNLK	A6
	Move.L	(A7)+,(A7)		; remove parameter
	RTS	


;-refs - MELT	MAIN	

CHECKCUR
	PEA	MouseRow(A5)
	_GetMouse
	CMPI	#CursorBound,MouseRow(A5)
	BLE	@4			; branch if mouse in upper screen

	BSET.B	#0,LowCursor(A5)	; mouse is low now
	BNE	@3			; skip if it was already

	_ShowCursor			; else show cursor and message

	CLR.L	-(A7)			; for string handle
	move.w	MouseCol(A5),D0		; Get mouse column
	ext.l	D0
	divu	#NumStrings,D0		; equal chance of each message
	swap	D0			; D0.W = remainder
	add.w	#StringBase,D0		; D0.W = STR resource number
	move.w	D0,-(A7)		; string #
	_GetString
	move.L	(A7)+,A0		; string handle
	move.L	(A0),-(A7)		; string pointer
	JSR	ANNOUNCE
@3	BRA	@5

@4	BCLR.B	#0,LowCursor(A5)	; Mouse is high now
	BEQ	@5			; skip if it was already

	_HideCursor			; else hide cursor and message

	LEA	MyTempRect(A5),A1	; A1 = our rectangle
	Move.L	A1,-(A7)		; push for _FillRect
	move.w	#CursorBound,(A1)+	; top of rect
	move.w	MyLeft(a5),(A1)+	; left of screen
	move.l	MyBotRight(a5),(A1)+	; bottom right of screen
	PEA	qdGlobals+black(a5)	; fill with black
	_FillRect

@5	RTS	


;-refs - MAIN	

INIT
	; Build array of pointers to RAWB 1-10
	CLR	D7
	LEA	glob46(A5),A3
@1	ADDQ	#1,D7
	CLR.L	-(A7)
	move.L	#'RAWB',-(A7)
	move.w	D7,-(A7)
	_GetResource
	move.L	(A7)+,A0
	MOVE.L	(A0),(A3)+
	CMPI	#9,D7
	BLE	@1

	CLR.L	-(A7)
	move.L	#'RAWB',-(A7)
	move.w	#20,-(A7)
	_GetResource
	move.L	(A7)+,A0
	MOVE.L	(A0),A4			; RAWB 20 pointer
	LEA	64(A4),A3
	LEA	glob39(A5),A0		; glob39[0]
	MOVE.L	A3,(A0)+		; glob39[0]
	MOVE.L	A4,(A0)+		; glob39[1]
	MOVE.L	A3,(A0)+		; glob39[2]
	MOVE.L	A4,(A0)+		; glob39[3]
	MOVE.L	A3,(A0)+		; glob39[4]
	MOVE.L	A4,(A0)+		; glob39[5]

	move.L	#WorkBytes,D0		; space for building images
	_NewPtr
	move.L	A0,WorkArea(a5)		; start of allocated memory

	CLR.B	LowCursor(A5)
	CLR.B	ShowDate(A5)

	PEA	qdGlobals(A5)
	_InitGraf

	PEA	MyPort(A5)
	_OpenPort

	_InitCursor

	_InitFonts
	move.w	#applFont,-(A7)		; use application font
	_TextFont
	move.w	#18,-(A7)		; in 18 point size
	_TextSize

	RTS	


MELTTICKS	equ	4		; ticks to delay in MELT

;-refs - MAIN	

MELT
	MOVEM.L D6-D7/A4,-(A7)
	moveq	#Steps-1,D7
	Move.L	WorkArea(A5),A4		; A4 = work area

@1	MOVE.L	#MELTTICKS,D6		; D6 = ticks to delay
	ADD.L	Ticks,D6		; D6 = tick time to delay until

	move.L	A4,-(A7)		; push pointer to painting area
	TST.B	ShowDate(A5)		; Show Date?
	BEQ	@2			; branch if not
	JSR	PROC34			; paint squares for showing date
	BRA	@3

@2	JSR	PROC33			; paint circles for showing time

@3	move.L	A4,-(A7)		; push pointer into work area
	move.L	PaintPtr(A5),-(A7)	; push pointer to screen image
	JSR	PROC31			; copy image to screen

@4	IF	MELTTICKS>1
	JSR	CHECKCUR
	ENDIF

	CMP.L	Ticks,D6		; Time to go on yet?
	BGT	@4			; Loop if not

	LEA	DispBytes(A4),A4	; next image in work area
	dbra	D7,@1

	moveq	#NumNum-1,D7
	LEA	NewData(A5),A0
	LEA	OldData(A5),A1
@8	MOVE.L	(A0)+,(A1)+
	DBRA	D7,@8

	MOVEM.L (A7)+,D6-D7/A4
	RTS


;-refs - MAIN	

SetData
	lea	Year(A5),A0		; input area
	TST.B	ShowDate(A5)		; Show date?
	bne	@1			; skip if so	
	lea	Hour(A5),A0		; else show time
@1	LEA	NewData(A5),A1		; output area
	moveq	#2,D1			; 3 fields to process
@0	move	(a0)+,D0		; D0 = field to process
	ext.l	D0			; clear high word
	divu	#10,D0			; divide by ten
	clr.w	(a1)+			; clear high word of output
	move.w	D0,(A1)+		; set decades
	swap	D0
	clr.w	(A1)+			; clear high word of output
	move.w	D0,(A1)+		; set units
	dbra	D1,@0

	RTS



;-refs - MAIN	

param1	set	76		; numeral data (glob46)
param2	set	72		; old data (OldData)
param3	set	68		; new data (NewData)
param4	set	64		; work area (WorkArea)
param5	set	60		; mask table pointers (glob39)
paramsize	set	20

PROC30
	move.L	(A7)+,A0		; A0 = return address
	MOVEM.L D0-D7/A0-A6,-(A7)	; sixty bytes onto stack
	
	Move.L	param4(A7),A5		; A5 = start of work area
	Lea	(NumNum-1)*NumWidth(A5),A3	; offset to right-most digit

	MOVE	#20,D0			; index to unit seconds or days
@1	MOVEA.L param1(A7),A4		; A4 = base of numeral data ptr array
	MOVEA.L param2(A7),A5		; old data
	MOVE.L	0(A5,D0.W),D6		; D6 = old digit
	LSL	#2,D6
	MOVEA.L 0(A4,D6.W),A1		; A1 = data for old digit
	MOVEA.L param3(A7),A5		; new data
	MOVE.L	0(A5,D0.W),D6		; D6 = new digit
	LSL	#2,D6
	MOVEA.L 0(A4,D6.W),A2		; A2 = data for new digit
	MOVEA.L param5(A7),A5
	MOVEA.L 0(A5,D0.W),A0		; A0 = ptr to RAWB 20 (perhaps +64)

	MOVE	#DispRows-1,D1
@2	MOVEA.L A3,A4
	MOVE	(A1)+,D2
	MOVE	(A1)+,D3
	MOVE	(A2)+,D4
	MOVE	(A2)+,D5
	ADDI	#15,D5
	SUB	D2,D4
	ASR	#3,D4
	SUB	D3,D5
	ASR	#3,D5

	MOVEQ	#Steps-1,D7
@3	MOVE	D2,D6
	LSR	#1,D6
	ANDI	#$FFF8,D6
	MOVEA.L A0,A5
	ADDA.W	D6,A5
	MOVE	D3,D6
	LSR	#1,D6
	ANDI	#$FFF8,D6
	MOVEA.L A0,A6
	ADDA.W	D6,A6
	MOVE.L	(A6)+,D6
	NOT.L	D6
	AND.L	(A5)+,D6
	MOVE.L	D6,(A4)+
	MOVE.L	(A6)+,D6
	NOT.L	D6
	AND.L	(A5)+,D6
	MOVE.L	D6,(A4)+
	ADD	D4,D2
	ADD	D5,D3
	ADDA.W	#DispBytes-NumWidth,A4	; next image
	DBRA	D7,@3

	MOVEA.L A3,A4
	MOVE	(A1)+,D2
	MOVE	(A1)+,D3
	MOVE	(A2)+,D4
	MOVE	(A2)+,D5
	ADDI	#15,D5
	SUB	D2,D4
	ASR	#3,D4
	SUB	D3,D5
	ASR	#3,D5

	MOVEQ	#Steps-1,D7
@4	MOVE	D2,D6
	LSR	#1,D6
	ANDI	#$FFF8,D6
	MOVEA.L A0,A5
	ADDA.W	D6,A5
	MOVE	D3,D6
	LSR	#1,D6
	ANDI	#$FFF8,D6
	MOVEA.L A0,A6
	ADDA.W	D6,A6
	MOVE.L	(A6)+,D6
	NOT.L	D6
	AND.L	(A5)+,D6
	OR.L	D6,(A4)+
	MOVE.L	(A6)+,D6
	NOT.L	D6
	AND.L	(A5)+,D6
	OR.L	D6,(A4)+
	ADD	D4,D2
	ADD	D5,D3
	ADDA.W	#DispBytes-NumWidth,A4	; next image
	DBRA	D7,@4

	ADDA.W	#DispWidth,A3		; next row
	DBRA	D1,@2

	SUB.W	#DispBytes+NumWidth,A3	; back to top, previous numeral
	SUBQ	#3,D0			; previous numeral
	DBMI	D0,@1

	MOVEM.L (A7)+,D0-D7/A0-A6	; restore all registers
	ADDA.W	#ParamSize,A7		; clear parameters
	JMP	(A0)



;-refs - MELT	

PROC31
	move.L	(A7)+,D0		; D0 = return address
	move.L	(A7)+,A0		; A0 = place to draw
	move.L	(A7)+,A1		; A1 = stuff to draw
	move.L	D0,-(A7)		; restore return address

	MOVEQ	#DispRows-1,D0		; rows in drawing
@1	move.l	A0,-(A7)		; save start of row
	MOVEQ	#DispWidth/4-1,D1	; width of drawing, in longs
@2	NOT.L	(A1)
	MOVE.L	(A1)+,(A0)+
	DBRA	D1,@2

	move.l	(A7)+,A0		; restore start of row
	ADDA.W	MyRowBytes(A5),A0	; skip to next row
	DBRA	D0,@1

	RTS	

; PROC33 paints the colons which separate portions of the time
;-refs - MELT	

Circle	dc.w	%0000001111000000
	dc.w	%0000011111100000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000011111100000
	dc.w	%0000001111000000

PROC33
	move.L	(A7)+,D0		; D0 = return address
	move.L	(A7)+,A0		; A0 = place to paint
	LEA	Circle,A1		; A1 = Circle image

	ADDA.W	#22*DispWidth+2*NumWidth-1,A0
				; 22 rows down, 2 chars in, back 8 pixels
	MOVE	#7,D2
@1	MOVE.B	(A1)+,D1		; D1.B = byte of image
	MOVE.B	D1,2*NumWidth(A0)	; top right (2 chars over)
	MOVE.B	D1,18*DispWidth(A0)	; bottom left (18 rows down)
	MOVE.B	D1,18*DispWidth+2*NumWidth(A0)	; bottom right (18 down, 2 over)
	MOVE.B	D1,(A0)+		; top left

	MOVE.B	(A1)+,D1		; D1.B = byte of image
	MOVE.B	D1,2*NumWidth(A0)	; top right (2 chars over)
	MOVE.B	D1,18*DispWidth(A0)	; bottom left (18 rows down)
	MOVE.B	D1,18*DispWidth+2*NumWidth(A0)	; bottom right (18 down, 2 over)
	MOVE.B	D1,(A0)+		; top left

	ADDA.W	#DispWidth-2,A0		; next row
	DBRA	D2,@1

	MOVEA.L D0,A0
	JMP	(A0)


; PROC34 paints the squares which separate portions of the date
;-refs - MELT	

Square	dc.w	%0000000000000000	; actually a rectangle
	dc.w	%0000000000000000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000
	dc.w	%0000111111110000

PROC34
	move.L	(A7)+,D0		; D0 = return address
	move.L	(A7)+,A0		; A0 = place to paint
	LEA	Square,A1		; A1 = Square

	ADDA.W	#32*DispWidth+2*NumWidth-1,A0
				; 32 rows down, 2 chars in, 8 pixels back
	MOVE	#7,D2
@1	MOVE.B	(A1)+,D1		; D1.B = byte of square
	MOVE.B	D1,2*NumWidth(A0)	; right (2 chars right)
	MOVE.B	D1,(A0)+		; left

	MOVE.B	(A1)+,D1		; D1.B = byte of square
	MOVE.B	D1,2*NumWidth(A0)	; right (2 chars right)
	MOVE.B	D1,(A0)+		; left

	ADDA.W	#DispWidth-2,A0		; next row
	DBRA	D2,@1

	MOVEA.L D0,A0
	JMP	(A0)



;-refs - MAIN	

GetTime
	move.l	Time,D0
	LEA	Year(A5),A0
	_Secs2Date
	Move.W	(A0),D0			; D0 = year
	Ext.L	D0			; clear high word
	Divu	#100,D0			; divide by century
	Swap	D0			; D0.W = year within century
	Move.W	D0,(A0)			; set decade+year, no centuries
	RTS