GENWiki

Premier IT Outsourcing and Support Services within the UK

User Tools

Site Tools


archive:computers:asmstr

Table of Contents

_STRUCTURED PROGRAMMING COLUMN_ by Jeff Duntemann

[LISTING ONE]

{ Calendar unit demo program } { Jeff Duntemann – 2/3/89 }

PROGRAM CalTest;

USES DOS,Crt, { Standard Borland units }

   Screens,    { Given in DDJ 4/89 }
   Calendar;   { Given in DDJ 6/89 }

CONST

YellowOnBlue = $1E; { Text attribute; yellow chars on blue background }
CalX         = 25;
CalY         = 5;

VAR

MyScreen   : ScreenPtr;  { Type exported by Screens unit }
WorkScreen : Screen;     { Type exported by Screens unit }
Ch         : Char;
Quit       : Boolean;
ShowFor    : DateTime;   { Type exported by DOS unit }
I          : Word;       { Dummy; picks up dayofweek field in GetDate }

BEGIN

MyScreen := @WorkScreen;    { Create a pointer to WorkScreen }
InitScreen(MyScreen,True);
ClrScreen(MyScreen,ClearAtom);     { Clear the entire screen }
Quit := False;
WITH ShowFor DO    { Start with clock date }
  GetDate(Year,Month,Day,I);
ShowCalendar(MyScreen,ShowFor,CalX,CalY,YellowOnBlue);
REPEAT                    { Until Enter is pressed: }
  IF Keypressed THEN      { If a keystroke is detected }
    BEGIN
      Ch := ReadKey;      { Pick up the keystroke }
      IF Ord(Ch) = 0 THEN { See if it's an extended keystroke }
        BEGIN
          Ch := ReadKey;  { If so, pick up scan code }
          CASE Ord(Ch) OF { and parse it }
            72 : Pan(MyScreen,Up,1);   { Up arrow }
            80 : Pan(MyScreen,Down,1); { Down arrow }
            75 : BEGIN                 { Left arrow; "down time" }
                   WITH ShowFor DO
                     IF Month = 1 THEN
                       BEGIN
                         Month := 12;
                         Dec(Year)
                       END
                     ELSE Dec(Month);
                   ShowCalendar(MyScreen,ShowFor,CalX,CalY,YellowOnBlue);
                 END;
            77 : BEGIN                 { Right arrow; "up time" }
                   WITH ShowFor DO
                     IF Month = 12 THEN
                       BEGIN
                         Month := 1;
                         Inc(Year)
                       END
                     ELSE Inc(Month);
                   ShowCalendar(MyScreen,ShowFor,CalX,CalY,YellowOnBlue);
                 END;
          END { CASE }
        END
      ELSE     { If it's an ordinary keystroke, test for quit: }
        IF Ch = Chr(13) THEN Quit := True
    END;
UNTIL Quit;
ClrScreen(MyScreen,ClearAtom)  { All this stuff's exported by Screens }

END.

[LISTING TWO]

{————————————————————–} { CALENDAR } { } { Text calendar for virtual screen platform } { } { by Jeff Duntemann KI6RA } { Turbo Pascal 5.0 } { Last modified 2/3/89 } {————————————————————–}

UNIT Calendar;

INTERFACE

USES DOS, { Standard Borland unit }

   TextInfo,  { Given in DDJ 3/89     }
   Screens,   { Given in DDJ 4/89     }
   CalCalc;   { Given in DDJ 6/89 courtesy Michael Covington }

TYPE

DaysOfWeek = (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
Months     = (January,February,March,April,May,June,July,
              August,September,October,November,December);

PROCEDURE ShowCalendar(Target : ScreenPtr;

                     ShowFor   : DateTime;
                     CalX,CalY : Integer;
                     Attribute : Byte);

IMPLEMENTATION

TYPE

String10 = STRING[10];

CONST

MonthNames : ARRAY[January..December] OF String10 =
('January','February', 'March','April','May','June','July',
 'August', 'September','October','November','December');
Days : ARRAY[January..December] OF Integer =
(31,28,31,30,31,30,31,31,30,31,30,31);

{$L CALBLKS} {$F+} PROCEDURE CalFrame; EXTERNAL;

    PROCEDURE Caldata;  EXTERNAL;

{$F-}

{$L BLKBLAST} {$F+} PROCEDURE BlkBlast(ScreenEnd,StoreEnd : Pointer;

                 ScreenX,ScreenY    : Integer;
                 ULX,ULY            : Integer;
                 Width,Height       : Integer;
                 Attribute          : Byte;
                 DeadLines          : Integer;
                 TopStop            : Integer);
        EXTERNAL;

{$F-}

FUNCTION IsLeapYear(Year : Integer) : Boolean;

{ Works from 1901 - 2199 }

BEGIN

IsLeapYear := False;
IF (Year MOD 4) = 0 THEN IsLeapYear := True

END;

PROCEDURE FrameCalendar(Target : ScreenPtr;

                      CalX,CalY : Integer;
                      Attribute : Byte;
                      StartDay  : DaysOfWeek;
                      DayCount  : Integer);

TYPE

PointerMath = RECORD
                CASE BOOLEAN OF
                  True  : (APointer : Pointer);
                  False : (OfsWord  : Word;
                           SegWord  : Word)
              END;

VAR

DataPtr    : Pointer;
FudgeIt    : PointerMath;
DayInset   : Word;
DayTopStop : Word;

BEGIN

{ DayInset allows is to specify which day of the week the first of the }
{ month falls.  It's an offset into the block containing day figures   }
DayInset := (7-Ord(StartDay))*4;
{ DayTopStop allows us to specify how many days to show in the month.  }
DayTopStop := 28+(DayCount*4)-DayInset;
BlkBlast(Target,@CalFrame,    { Display the calendar frame            }
         VisibleX,VisibleY,   { Genned screen size from TextInfo unit }
         CalX,CalY,           { Show at specified coordinates         }
         29,17,               { Size of calendar frame block          }
         Attribute,           { Attribute to use for calendar frame   }
         0,                   { No interspersed empty lines           }
         0);                  { No topstop; show the whole thing.     }
WITH FudgeIt DO { FudgeIt is a free union allowing pointer arithmetic }
  BEGIN
    APointer := @CalData;     { Create the pointer to the days block  }
    OfsWord  := OfsWord+DayInset; { Offset into block for start day   }
    BlkBlast(Target,APointer,     { Blast the day block over the      }
             VisibleX,VisibleY,   {   calendar frame }
             CalX+1,CalY+5,       { Pos. of days relative to frame    }
             28,6,                { Size of day block }
             Attribute,           { Show days in same color as frame  }
             1,                   { Insert 1 line between block lines }
             DayTopStop)          { Set limit on number of chars to   }
  END                             { be copied from block to control   }

END; { how many days shown for a month }

PROCEDURE ShowCalendar(Target : ScreenPtr;

                     ShowFor   : DateTime;
                     CalX,CalY : Integer;
                     Attribute : Byte);

CONST

NameOffset : ARRAY[January..December] OF Integer =
(8,8,10,10,11,10,10,9,7,8,8,8);

VAR

StartDay    : DaysOfWeek;
TargetMonth : Months;
TargetDay   : Real;
DaysInMonth : Integer;

BEGIN

{ First figure day number since 1980: }
WITH ShowFor DO TargetDay := DayNumber(Year,Month,1);
{ Then use the day number to calculate day-of-the-week: }
StartDay := DaysOfWeek(WeekDay(TargetDay)-1);
TargetMonth := Months(ShowFor.Month-1);
DaysInMonth := Days[TargetMonth];
{ Test and/or adjust for leap year: }
IF TargetMonth = February THEN
  IF IsLeapYear(ShowFor.Year) THEN DaysInMonth := 29;
{ Now draw the frame on the virtual screen! }
FrameCalendar(Target,
              CalX,CalY,
              Attribute,
              StartDay,
              DaysInMonth);
{ Add the month name and year atop the frame: }
GotoXY(Target,CalX+NameOffset[TargetMonth],CalY+1);
WriteTo(Target,MonthNames[TargetMonth]+' '+IntStr(ShowFor.Year,4));

END;

END.

[LISTING THREE]

UNIT CalCalc;

{ — Calendrics — }

{ Long-range calendrical package in standard Pascal } { Copyright 1985 Michael A. Covington }

INTERFACE

function daynumber(year,month,day:integer):real;

procedure caldate(date:real; var year,month,day:integer);

function weekday(date:real):integer;

function julian(date:real):real;

IMPLEMENTATION

function floor(x:real) : real;

{ Largest whole number not greater than x.           }
{ Uses real data type to accommodate large numbers.  }

begin

if (x < 0) and (frac(x) <> 0) then
  floor := int(x) - 1.0
else
  floor := int(x)

end;

function daynumber(year,month,day:integer):real;

{ Number of days elapsed since 1980 January 0 (1979 December 31). }
{ Note that the year should be given as (e.g.) 1985, not just 85. }
{ Switches from Julian to Gregorian calendar on Oct. 15, 1582.    }

var

y,m:   integer;
a,b,d: real;

begin

if year < 0 then y := year + 1
            else y := year;
m := month;
if month < 3 then
  begin
    m := m + 12;
    y := y - 1
  end;
d := floor(365.25*y) + int(30.6001*(m+1)) + day - 723244.0;
if d < -145068.0 then
  { Julian calendar }
  daynumber := d
else
  { Gregorian calendar }
  begin
    a := floor(y/100.0);
    b := 2 - a + floor(a/4.0);
    daynumber := d + b
  end

end;

procedure caldate(date:real; var year,month,day:integer);

{ Inverse of DAYNUMBER; given date, finds year, month, and day.   }
{ Uses real arithmetic because numbers are too big for integers.  }

var

a,aa,b,c,d,e,z: real;
y: integer;

begin

z := int(date + 2444239.0);
if date < -145078.0 then
  { Julian calendar }
  a := z
else
  { Gregorian calendar }
  begin
    aa := floor((z-1867216.25)/36524.25);
    a := z + 1 + aa - floor(aa/4.0)
  end;
b := a + 1524.0;
c := int((b-122.1)/365.25);
d := int(365.25*c);
e := int((b-d)/30.6001);
day := trunc(b - d - int(30.6001*e));
if e > 13.5 then month := trunc(e - 13.0)
            else month := trunc(e - 1.0);
if month > 2 then y := trunc(c - 4716.0)
             else y := trunc(c - 4715.0);
if y < 1 then year := y - 1
         else year := y

end;

function weekday(date:real):integer;

{ Given day number as used in the above routines,   }
{ finds day of week (1 = Sunday, 2 = Monday, etc.). }

var

dd: real;

begin

dd := date;
while dd > 28000.0 do dd:=dd-28000.0;
while dd < 0 do dd:=dd+28000.0;
weekday := ((trunc(dd) + 1) mod 7) + 1

end;

function julian(date:real):real;

{ Converts result of DAYNUMBER into a Julian date. }

begin

julian := date + 2444238.5

end;

END. { CalCalc }

[LISTING FOUR]

;

; ; B L K B L A S T - Blast 2D character pattern and attributes into memory ; ;

; ; by Jeff Duntemann 3 February 1989 ; ; BLKBLAST is written to be called from Turbo Pascal 5.0 using the EXTERNAL ; machine-code procedure convention. ; ; This version is written to be used with the SCREENS.PAS virtual screens ; unit for Turbo Pascal 5.0. See DDJ for 4/89. ; ; Declare the procedure itself as external using this declaration: ; ; PROCEDURE BlkBlast(ScreenEnd,StoreEnd : Pointer; ; ScreenX,ScreenY : Integer; ; ULX,ULY : Integer; ; Width,Height : Integer; ; Attribute : Byte; ; DeadLines : Integer; ; TopStop : Integer); ; EXTERNAL; ; ; The idea is to store a video pattern as an assembly-language external or ; as a typed constant, and then blast it into memory so that it isn't seen ; to "flow" down from top to bottom, even on 8088 machines. ; ; During the blast itself, the attribute byte passed in the Attribute ; parameter is written to the screen along with the character information ; pointed to by the source pointer. In effect, this means we do a byte-sized ; read from the source character data, but a word-sized write to the screen. ; ; The DeadLines parm specifies how many screen lines to skip between lines of ; the pattern. The skipped lines are not disturbed. TopStop provides a byte ; count that is the maximum number of bytes to blast in from the pattern. ; If a 0 is passed in TopStop, the value is ignored. ; ; To reassemble BLKBLAST: ; ; Assemble this file with MASM or TASM: "C>MASM BLKBLAST;" ; (The semicolon is unnecessary with TASM.) ; ; No need to relink; Turbo Pascal uses the .OBJ only. ; ;

; ; STACK PROTOCOL ; ; This creature puts lots of things on the stack. Study closely: ;

ONSTACK STRUC OldBP DW ? ;Caller's BP value saved on the stack RetAddr DD ? ;Full 32-bit return address. (This is a FAR proc!) TopStop DW ? ;Maximum number of chars to be copied from block pattern DeadLns DW ? ;Number of lines of dead space to insert between blasted lines Attr DW ? ;Attribute to be added to blasted pattern BHeight DW ? ;Height of block to be blasted to the screen BWidth DW ? ;Width of block to be blasted to the screen ULY DW ? ;Y coordinate of upper left corner of the block ULX DW ? ;X coordinate of the upper left corner of the block YSize DW ? ;Genned max Y dimension of current visible screen XSize DW ? ;Genned max X dimension of current visible screen Block DD ? ;32-bit pointer to block pattern somewhere in memory Screen DD ? ;32-bit pointer to an array of pointers to screen lines ENDMRK DB ? ;Dummy field for stack struct size calculation ONSTACK ENDS

CODE SEGMENT PUBLIC

      ASSUME  CS:CODE
      PUBLIC  BlkBlast

BlkBlast PROC FAR

       PUSH    BP               ;Save Turbo Pascal's BP value
       MOV     BP,SP            ;SP becomes new value in BP
       PUSH    DS               ;Save Turbo Pascal's DS value

;————————————————————————- ; If a zero is passed in TopStop, then we fill the TopStop field in the ; struct with the full size of the block, calculated by multiplying ; BWidth times BHeight. This makes it unnecessary for the caller to ; pass the full size of the block in the TopStop parameter if topstopping ; is not required. ;————————————————————————-

       CMP     [BP].TopStop,0   ; See if zero was passed in TopStop
       JNZ     GetPtrs          ; If not, skip this operation
       MOV     AX,[BP].BWidth   ; Load block width into AX
       MUL     [BP].BHeight     ; Multiply by block height, to AX
       MOV     [BP].TopStop,AX  ; Put the product back into TopStop

;————————————————————————- ; The first important task is to get the first pointer in the ShowPtrs ; array into ES:DI. This involved two LES operations: The first to get ; the pointer to ShowPtrs (field Screen in the stack struct) into ES:DI, ; the second to use ES:DI to get the first ShowPtrs pointer into ES:DI. ; Remembering that ShowPtrs is an *array* of pointers, the next task is ; to index DI into the array by multiplying the top line number (ULY) ; less one (because we're one-based) by 4 using SHL and then adding that ; index to DI: ;————————————————————————- GetPtrs: LES DI,[BP].Screen ; Address of ShowPtrs array in ES:DI

       MOV     CX,[BP].ULY      ; Load line address of block dest. to CX
       DEC     CX               ; Subtract 1 'cause we're one-based
       SHL     CX,1             ; Multiply CX by 4 by shifting it left...
       SHL     CX,1             ;  ...twice.
       ADD     DI,CX            ; Add the resulting index to DI.
       MOV     BX,DI            ; Copy offset of ShowPtrs into BX
       MOV     DX,ES            ; Copy segment of ShowPtrs into DX
       LES     DI,ES:[DI]       ; Load first line pointer into ES:DI

;————————————————————————- ; The inset from the left margin of the block's destination is given in ; struct field ULX. It's one-based, so it has to be decremented by one, ; then multiplied by two using SHL since each character atom is two bytes ; in size. The value in the stack frame is adjusted (it's not a VAR parm, ; so that's safe) and then read from the frame at the start of each line ; blast and added to the line offset in DI. ;————————————————————————-

       DEC     [BP].ULX         ; Subtract 1 'cause we're one-based
       SHL     [BP].ULX,1       ; Multiply by 2 to cover word moves
       ADD     DI,[BP].ULX      ; And add the adjustment to DI

;————————————————————————- ; One additional adjustment must be made before we start: The Deadspace ; parm puts 1 or more lines of empty space between each line of the block ; that we're blasting onto the screen. This value is passed in the ; DEADLNS field in the struct. It's passed as the number of lines to skip, ; but we have to multiply it by 4 so that it becomes an index into the ; ShowPtrs array, each element of which is four bytes in size. Like ULX, ; the value is adjusted in the stack frame and added to the stored offset ; value we keep in DX each time we set up the pointer in ES:DI to blast the ; next line. ;————————————————————————-

       SHL     [BP].DEADLNS,1   ; Shift dead space line count by 1...
       SHL     [BP].DEADLNS,1   ; ...and again to multiply by 4
       LDS     SI,[BP].Block    ; Load pointer to block into DS:SI

;————————————————————————- ; This is the loop that does the actual block-blasting. Two counters are ; kept, and share CX by being separate values in CH and CL. After ; each line blast, both pointers are adjusted and the counters swapped, ; the LOOP counter decremented and tested, and then the counters swapped ; again. ;————————————————————————- MovEm: MOV CX,[BP].BWidth ; Load atom counter into CH

       MOV     AH,BYTE PTR [BP].Attr     ; Load attribute into AH

DoChar: LODSB ; Load char from block storage into AL

       STOSW               ; Store AX into ES:DI; increment DI by 2
       LOOP    DoChar      ; Go back for next char if CX > 0

;————————————————————————- ; Immediately after a line is blasted from block to screen, we adjust. ; First we move the pointer in ES:DI to the next pointer in the ; Turbo Pascal ShowPtrs array. Note that the source pointer does NOT ; need adjusting. After blasting through one line of the source block, ; SI is left pointing at the first character of the next line of the ; source block. Also note the addition of the deadspace adjustment to ; BX *before* BX is copied into DI, so that the adjustment will be ; retained through all the rest of the lines moved. Finally, we subtract ; the number of characters in a line from TopStop, and see if there are ; fewer counts left in TopStop than there are characters in a block line. ; If so, we force BWidth to the number of remaining characters, and ; BHeight to one, so that we will blast only one remaining (short) line. ;————————————————————————-

       MOV     ES,DX           ; Copy ShowPtrs segment from DX into ES
       ADD     BX,4            ; Bounce BX to next pointer offset
       ADD     BX,[BP].DeadLns ; Add deadspace adjustment to BX
       LES     DI,ES:[BX]      ; Load next pointer into ES:DI
       ADD     DI,[BP].ULX     ; Add adjustment for X offset into screen
       MOV     AX,[BP].TopStop ; Load current TopStop value into AX
       SUB     AX,[BP].BWidth  ; Subtract BWidth from TopSTop value
       JBE     GoHome          ; If TopStop is <= zero, we're done.
       MOV     [BP].TopStop,AX ; Put TopStop value back in stack struct
       CMP     AX,[BP].BWidth  ; Compare what remains in TopStop to BWidth
       JAE     MovEm           ; If at least one BWidth remains, loop again
       MOV     [BP].BWidth,AX  ; Otherwise, replace BWidth with remainder
       JMP     MovEm           ;   and jump to last go-thru

;————————————————————————- ; When the outer loop is finished, the work is done. Restore registers ; and return to Turbo Pascal. ;————————————————————————-

GoHome: POP DS ; Restore Turbo Pascal's

      MOV     SP,BP             ; Restore Turbo Pascal's stack pointer...
      POP     BP                ; ...and BP
      RET     ENDMRK-RETADDR-4  ; Clean up stack and return as FAR proc!
                                ;   (would be ENDMRK-RETADDR-2 for NEAR...)

BlkBlast ENDP CODE ENDS

       END

[LISTING FIVE]

       TITLE  CalBlks -- External calendar pattern blocks

; By Jeff Duntemann – TASM 1.0 – Last modified 3/1/89 ; ; For use with CALENDAR.PAS and BLKBLAST.ASM as described in DDJ 6/89

CODE SEGMENT WORD

       ASSUME CS:CODE

CalFrame PROC FAR

       PUBLIC CalFrame
       DB   'ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸'
       DB   '³                           ³'
       DB   'ÃÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ´'
       DB   '³Sun³Mon³Tue³Wed³Thu³Fri³Sat³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´'
       DB   '³   ³   ³   ³   ³   ³   ³   ³'
       DB   'ÔÍÍÍÏÍÍÍÏÍÍÍÏÍÍÍÏÍÍÍÏÍÍÍÏÍÍ;'

Calframe ENDP

CalData PROC FAR

       PUBLIC CalData
       DB   '   ³   ³   ³   ³   ³   ³   ³'
       DB   '  1³  2³  3³  4³  5³  6³  7³'
       DB   '  8³  9³ 10³ 11³ 12³ 13³ 14³'
       DB   ' 15³ 16³ 17³ 18³ 19³ 20³ 21³'
       DB   ' 22³ 23³ 24³ 25³ 26³ 27³ 28³'
       DB   ' 29³ 30³ 31³   ³   ³   ³   ³'
       DB   '   ³   ³   ³   ³   ³   ³   ³'
       DB   '   ³   ³   ³   ³   ³   ³   ³'

CalData ENDP

CODE ENDS

       END
/data/webs/external/dokuwiki/data/pages/archive/computers/asmstr.txt · Last modified: 2001/11/08 10:19 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki