TweetFollow Us on Twitter

Record Definitions
Volume Number:5
Issue Number:2
Column Tag:Forth Forum

Record Definitions

By Jörg Langowski, MacTutor Editorial Staff

“Record definitions in Mach2”

Record structures and arrays are not part of standard Forth implementations. More than two years ago, in V2#7, I had given an example how to implement records. Mach2 has evolved since then, and so have ways of implementing new data structures, as you can see in the Object Forth project by Wayne Joerding that we recently discussed. For those of you who do not want a full object-oriented system, but still ways of defining data structures in an easy way, I have found two examples on the GEnie bulletin boards. Those examples show two fundamentally different approaches to deal with record definitions.

‘Local’ field names - method 1

The problem in setting up the Forth compiler to deal with record definition in a proper way is somewhat similar to implementing an object-oriented programming system. That is, just like a message is local to an object, and the same message may cause different effects on different objects, a field name should be local to a record. In the Pascal record definitions

\1

rec1 = record
 x: real;
 i: integer;
 y: real;
 end;

rec2 = record
 y: real;
 j: integer;
 x: real;
 end;

the field x would create a different offset into a record of type rec2 than for a rec1 type; and rec1.i, rec2.j would be valid while rec1.j, rec2.i would not. So if we define a field name as some kind of Forth word, this word should be in some ‘local vocabulary’ that belongs to the record definition and is only visible while the field reference is resolved.

The other requirement is that we should be able to pass a record as a parameter to a routine, so that given the pointer to a record on the stack, a Forth definition would know how to resolve the field reference. In a strongly typed language like Pascal this is easy; field references into record formal parameters can be resolved at compile time because the procedure arguments are of defined type. In Forth, typically the address of a data structure would be passed on the stack. However, at compile time there is no way we can restrict the type of argument that this address might later point to at run time! This problem could only be solved by type checking built into the record definition and deferring the resolution of the field reference to run time, some sort of ‘late binding’.

The first method of record definition (Listing 1), written by Waymen Askey of Palo Alto Shipping (I added some minor modifications, like floating point and array support), creates a local dictionary for each record template in the Forth dictionary space. When a record template is defined, using the syntax

\2

template rec1
 :real x
 :word i
 :byte c
tend  

its field names x, i and c are compiled into the dictionary together with relevant information for resolving the references. At the end of the template declaration, the dictionary links are changed in such a way that the ‘local’ names are skipped when the dictionary is searched. Let’s declare a record:

\3

rec1 structure myRec

A field of this record is later accessed by using the structure fetch/store words, s@ and s!.

myRec x s@ will put the value in field x of myrec on the floating point stack, and myRec i s@ will put the word value of field i on the stack. The trick Waymen used was to build some intelligence into the fetch/store words. When the record and field words, myRec and x for example, are executed or compiled into a definition, field type and offset are determined and kept in global variables. The s@ word will check these variables and know how to access the field, whether - in immediate execution - to do a byte, word or long word fetch, addressing into an array, or a ten-byte fetch onto the floating point stack for a real number; or at compile time create code that will do these things later.

The drawback of this approach is that field references can only be resolved at compile or immediate execution time. If we wanted to write a word that operates on a record whose address is passed on the stack, we couldn’t use the field names that were defined in the record template - they are only valid right after a record name was executed or compiled. Therefore, a definition like

\4

: getX { myRec -- } myRec x s@ ;  

must fail because myRec is a local variable, not a record name.

An example how to use this method of record declaration with various field types is given at the end of the listing. You see the drawback: Even though the record fields wavelength, temperature, and angle are all themselves structures of the same type parameter, there is no way to factor out the common code in

5

 cr curve1 wavelength name s^ count type .” = “
 curve1 wavelength value s@ f.
 curve1 wavelength unit s^ count type
 cr curve1 temperature name s^ count type .” = “
 curve1 temperature value s@ f.
 curve1 temperature unit s^ count type
 cr curve1 angle name s^ count type .” = “
 curve1 angle value s@ f.
 curve1 angle unit s^ count type

by using a word that would just print name, value and unit of any given parameter. If this problem was resolved, the record compiler would almost be perfect.

‘Global’ field names - method 2

Listing 2 shows a much simpler approach to structure definitions that does not do type checking. I downloaded this code from the Forth Roundtable on GEnie, and unfortunately have not the slightest idea who the author is. All I could find out was that the original code was probably posted on the East Coast Forth Board.

However, since this code solves one of our problems, record passing as formal parameters, I’d like to print it here. Its strategy is much more like that of the structure words built into MacForth Plus. Here, a record template is defined like

\6

RECORD Rectangle
        Global  SHORT: Top
        Global  Short: Left
        Global  Short: Bottom
        Global  SHORT: RIght
ENd.RECORD
 
Variable myRect Rectangle 4 - VALLOT ;

so the record name, when executed, simply leaves the record length on the stack for later ALLOT or VALLOT. The field names are words which add the field offset to an existing address on the stack, so they can be used in any context. We have to check ourselves whether the address is a valid record address and whether the field referenced actually exists in that record (if we care at all). All field names are global, and therefore must be unique; no two different record declarations can have fields of the same name at different offsets.

This approach is not so different from the very basic one that I used in most of my examples, where I simply defined field names as constants and added the offset to the record address.

What the Macintosh Forth world needs is really a combination of the two approaches, with type checking at compile time and local field names for convenience, and a possibility to resolve field references on record addresses at compile time without too much overhead. If one knew the type of the record passed on the stack ahead of time (which is usually the case), one could probably define some ‘field reference resolution word’ which computes an offset given a template and a field name. I hope I can show you an example in one of my next columns.

Upcoming: an update to Wayne Joerding’s Object Forth, and a review of PocketForth, a public domain 16-bit Forth that comes as an application and a desk accessory. Stay tuned.

Listing 1: Structure definitions with local field names
\ STRUCTUREs 2.5   for the Macintosh  MACH2
\ Jan 3, 1987 by Waymen Askey 
\ edited, floating point & array addition by 
\ J. Langowski @ MacTutor
\ This MACH2 extension is released for the public good; 
\ however, for those planning commercial use of this code, 
\ please notify  me so that I might know of its intended use.
\              Waymen Askey @ PASC
\  also GEnie MACH2 RoundTable.

only mac also sane also forth definitions
( VARIABLES used in STRUCTURE 2.5 )
decimal
variable current.template
variable op.type
variable A5offset ( holds the A5 offset to a structure )

( CODE word utilities used in STRUCTURE 2.5 )
code var.link  ( -- a | variable link pointer )
 lea $F7F8(A5),A0
 move.l A0,-(A6)
 rts
end-code

code a5@  ( -- a )
 move.l A5,-(A6)
 rts
end-code mach
  
code get.field  ( a1 a2 -- a3 -1 or 0 | searches templates )
  ( a1=template, a2= pad, a3=field pointer, 0 if not found )
 move.l (A6)+,D2
 move.l (A6)+,D3
 moveq.l #0,D1
 moveq.l #0,D0
@start  movea.l D3,A1
        movea.l D2,A0
        move.b (A1)+,D1  ( link to next field )
        beq.s @end       ( if link=0, field not found )
        move.b (A1),D0
@loop   cmpm.b (A1)+,(A0)+
        dbne D0,@loop
        beq.s @found
        add.l D1,D3   ( increment field pointer )
        bra.s @start
@found  movea.l D3,A1
        move.b 1(A1),D1  ( get string count )
        addq.w #2,D1
        btst #0,D1  ( test for odd count )
        beq.s @even
        addq.w #1,D1
@even   add.l D1,D3
        moveq.l #-1,D1
        move.l D3,-(A6)
@end    move.l D1,-(A6)
        rts
end-code

code >sr  ( n -- | push value onto subroutine stack )
 move.l (A6)+,-(A7)
 rts
end-code mach

code sr>  ( -- n | pop value from subroutine stack )
 move.l (A7)+,-(A6)
 rts
end-code mach

code sr@  ( -- n | copy value from subroutine stack )
 move.l (A7),-(A6)
 rts
end-code mach

( Miscellaneous utility words used in STRUCTURE 2.5 )
: >even  ( a -- a’ | 
 word aligns address, i.e. rounds up to even)
 dup  1 and  + ;

: >odd  ( a -- a’ | odd aligns address, rounds up to odd )
 1 or ;

: needed  ( n -- | checks for at least n items on stack )
 depth 1- > abort” Missing needed stack item(s)! “ ;

( Brute-force machine code words )
: ncode,  
( n1...n -- | machine code defining word, stuffs n words )
 create   dup needed   dup 2* w,   
 0 do   w,   loop
 does>   ( -- | compiles machine code )
 dup   2+ swap   dup w@   +   
 do   i  w@  w,   -2 +loop ;  

hex
( define some machine code “stuff” words )
41ED 1 ncode,  lea_d(a5),a0      
4EBA 1 ncode, jsr_d(PC)
4EAD 1 ncode, jsr_d(A5)
( LEA and JSR also need a word of extension for displacement )
2D3C 1 ncode, move.l_#,-(A6)  
 ( plus a long extension for # )
2D08 1 ncode,  move.l_a0,-(a6)     
4E75 1 ncode,  rts,
( The following expect an address to be in A0 )
7000 1010 2D00 3 ncode, byte@
7000 3010 2D00 3 ncode, word@
2D10 1 ncode, long@
201E 1080 2 ncode, byte!
201E 3080 2 ncode, word!
209E 1 ncode, long!
\ disassemble the following to check how they work.
\ Exercise for the reader... - JL
5187 5587 2247 22d8 22d8 32d8 6 ncode, real@
2247 20d9 20d9 30d9 5087 5487 6 ncode, real!
201e e580 2d30 0000 4 ncode, array@
201e e580 219e 0000 4 ncode, array!
201e e380 4281 3230 0000 2d01 6 ncode, warray@
201e e380 221e 3181 0000 5 ncode, warray!
decimal

( Dictionary header, name, and struct link words )
: link>name   ( lfa -- ‘nf | ‘nf points to header length byte)
 4 + ;
 
: name.count   
 ( ‘nf -- ‘nf+1  n | dictionary header name count)
 count 31 and ;

: link>segment  
 ( lfa -- ‘sf | ‘sf is the dictionary segment field address)
 link>name name.count  +  >even ;
 
: link>parameter  
 ( lfa -- ‘pf | ‘pf is the parameter field pointer)
 link>segment 2+ ;

: link>struct  ( lfa -- struct.fields )
 link>segment 4 + ;
 
: jsr_d(PC),  ( lfa -- | compiles PC relative JSR)
 jsr_d(PC)
 link>body here -  w, ;
 
: jsr_d(A5),  
 ( lfa -- | compiles A5 relative JSR, i.e. jump table )
 jsr_d(A5)  
 link>parameter w@  w, ;
 
: struct.zero  ( -- lfa | returns lfa of struct.zero )
 “ struct.zero” find  drop ;

: nallot  ( n -- | allots n bytes in name space )
 np +! ; 
  
: name,   ( -- parses and compiles text into name space.)
 32 word  np @  over c@ 1+  dup >odd nallot  cmove ;
 
: nc,  ( n -- | compiles byte into name space )
 np @ c!   1 nallot ;
 
: nw,  ( n -- | compiles word into name space )
 np @ w!   2 nallot ;
 
: n,  ( n -- | compiles long into name space )
 np @ !   4 nallot ;
 
( TEMPLATE, STRUCTURE and field words )
: struct.error  ( -- )
  cr pad count type 
  .”  ?  Error, unknown field or incomplete structure path! “
  abort ;
  
global 
: template  ( -- here 0 | begins TEMPLATE definition )   
  create here 0   2 allot 
  does>  ( -- template.size ) 
    dup w@ swap 4 - body>link   current.template ! ;
 
: tend  
 ( here n -- | (T)emplate(END) ends template definition  )
  swap w!   0 nw, ;
  
global 
: afield  ( size op.type --  )
  create  w,  >even w,
  does>  ( here Toffset -- here new.Toffset )
         ( Toffset means (T)emplate(OFFSET) )  
    2dup 2+   w@  + >sr  
    w@  np @ >sr  1 nallot  name,  
    0 nc, ( field type=0 )   nc, ( op.type )   
    nw, ( Toffset )   np @ sr@ - sr> c! ( field link )
    sr> ;
  
( The following op.types are reserved and defined below )
( 06 byte, 12 word, 18 long, 24 string, 
 30 real, 36 struct, 42 array, 48 warray )

( size.in.bytes op.type  AFIELD  named.afield.type )
1  06 afield  :byte   
2  12 afield  :word
4  18 afield  :long
10 30 afield  :real

: :string  ( here Toffset size -- here Toffset+size+1  )
 3 needed  1+   over +   >even swap   
 np @ >sr  1 nallot  name,   
 0 nc, ( field type=0 )   24 nc, ( op.type=24) 
   nw, ( Toffset )   np @ sr@ - sr> c! ( field link ) ;     

: :array  ( here Toffset size -- here Toffset+size+1  )
 3 needed  4* over +   swap   np @ >sr  1 nallot  name,   
 0 nc, ( field type=0 )   42 nc, ( op.type=42) 
  nw, ( Toffset )   np @ sr@ - sr> c! ( field link ) ;     

: :warray  ( here Toffset size -- here Toffset+size+1  )
 3 needed  2* over +   swap   np @ >sr  1 nallot  name,   
 0 nc, ( field type=0 )   48 nc, ( op.type=48) 
  nw, ( Toffset )   np @ sr@ - sr> c! ( field link ) ;     

: :struct  ( here Toffset size -- here Toffset+size  )
 3 needed  over +   >even swap   
 np @ >sr  1 nallot  name,  
 06 nc, ( field type=06 )  36 nc, ( op.type=36 )
 nw, ( Toffset )
 current.template @  struct.zero - n, ( template link )  
  np @ sr@ - sr> c! ( field link ) ;  

: >pad  ( a -- | moves string to pad )
  pad over c@ 1+  cmove ;

: make.var.link  { | name.pointer var.pointer vlink --  }
 np @ -> name.pointer  var.link @ -> var.pointer   
 name.pointer var.link ! 
 name.pointer var.pointer -    -> vlink
 name.pointer dup 1 and + -> name.pointer
 vlink name.pointer !
 name.pointer 4 + np ! ;

( Decision table for field type decode )
: do.afield ( ^field.type --  true )
 1+ dup c@ op.type !   1+ w@ A5offset +!   -1 ;

: do.bfield  ( ^field.type -- new.template false )
 dup 1+ dup c@ op.type !   1+ w@ A5offset +!
 4 + @   struct.zero +   link>struct   0 ; 

: rts rts, ; immediate
 
( DO.FIELD table entries decode field data )
( afield’s are simple :BYTE, :WORD, 
 :LONG, :STRING types )
( bfield’s are :STRUCT fields )

create do.field  ( field_type  table_offset/type )
]do.afield rts  (   afield         0            )
 do.bfield rts  (   bfield         6            )
[                ( end of current table          )

global
: make.struct  (  template.link A5offset  -- )   
( This is the word which must resolve a structure reference. )
  A5offset !  ( A5 displacement for the struct )
  36 op.type !  ( set default op.type to struct )
  struct.zero +  link>struct  ( template.address -- )
  begin    
    32 word   >pad
    pad get.field        
    if  ( field found )
      dup  c@ do.field +  execute
    else ( field not found )
      pad find 1 = 
      if 
        link>body   execute  -1
      else 
        struct.error
      then 
    then  
  until ;

hex
: structure  
( n -- | creates structure alloting n bytes in variable space )
  1 needed create   immediate make.var.link   
  -4 allot lea_d(a5),a0  vp @ w,  ( variable-like beginning )
  move.l_#,-(A6)  current.template @ struct.zero - ,    
  move.l_#,-(A6)   vp @ ,  
  “ make.struct” find drop dup link>segment  w@ 0=
  if  jsr_d(PC),  else  jsr_d(A5), then
  rts,   
  vallot ; 
decimal

( STRUCTURE operators )
: compileA5  ( -- | compiles A5 reference )
  lea_d(a5),a0  a5offset @ w, ;

: pushA5  ( -- | executes A5 var reference )
  a5offset @ a5@ + ;

: do.bit  ( -- )  ( I’m lazy, define your own.  W. Askey )
  cr .” BIT operations are yet undefined!” abort ;
 
: do.struct  ( -- )  ( Fetch/store doesn’t make sense here. )   
  cr .” STRUCTURE fetch/store operations are undefined! “ abort ;
  
: do.string  ( -- )  ( If you wish, define your own. )
  cr .” STRING fetch/store operations are undefined! “ abort ;
  
: do.byte@  ( f -- )
  if compileA5  byte@ else  pushA5 c@ then ;
 
: do.word@  ( f -- )
  if compileA5  word@ else pushA5 w@ then ;
  
: do.long@  ( f -- )
  if compileA5 long@ else pushA5 @ then ;
 
: do.array@  ( idx f -- )
  if compileA5 array@ else 4* pushA5 + @ then ;

: do.warray@  ( idx f -- )
  if compileA5 warray@ else 2* pushA5 + w@ then ;

: do.real@  ( f -- )
  if compileA5 real@ else pushA5 f@ then ;

 ( Decision table for fetch )
 create op.table@   ( op.types are offsets into this table ) 
 ]  do.bit rts      ( op.type = 0  )
    do.byte@ rts    (  “  “   = 6  )
    do.word@ rts    (  “  “   = 12 )
    do.long@ rts    (  “   “  = 18  etc, etc. )
    do.string rts
    do.real@ rts
    do.struct rts
 do.array@ rts
 do.warray@ rts
[

: do.byte!  ( f -- )
  if compileA5  byte! else pushA5 c! then ;
 
: do.word!  ( f -- )
  if compileA5  word! else pushA5 w! then ;
  
: do.long!  ( f -- )
  if compileA5 long! else pushA5 ! then ;

: do.array!  ( idx f -- )
  if compileA5 array! else 4* pushA5 + ! then ;

: do.warray!  ( idx f -- )
  if compileA5 warray! else 2* pushA5 + w! then ;

: do.real!  ( f -- )
  if compileA5 real! else pushA5 f! then ;

create op.table!  ( decision table for store )
]do.bit rts
 do.byte! rts
 do.word! rts
 do.long! rts
 do.string rts
 do.real! rts
 do.struct rts
 do.array! rts
 do.warray! rts
[
  
: s^  ( -- a | returns pointer to structure field )
( ALL field types are allowed. i.e. strings, struct, etc. )
 state @ 
 if  compileA5 move.l_a0,-(a6) else pushA5 then 
; immediate

: s@  ( -- data | Fetch field contents, data type smart)
  state @
  op.type @ op.table@ + execute ; immediate

: s!  ( data -- | Store into field, data type smart)
  state @
  op.type @ op.table! + execute ; immediate
 
: stype  ( -- op.type | returns the op.type of a field )
 op.type @  state @ 
  if [compile] literal then 
; immediate
( Examples of structure usage.  Data Storage is limited to the approximately 
32K global area referenced off of register A5 -- just as for regular 
MACH2 variables. Structure references have a REQUIRED syntax, it is best 
NOT to use any non-STRUCTURE Forth words when between field names in 
a structure calling sequence.  That is, please end each structure reference 
prior to any DUP’s, SWAP’s, etc. The structure pointer operator -- S^ 
-- may be used at any place in the structure calling sequence.  S^ will 
return the address of the field or structure itself.  Structures MUST 
be terminated with a defined structure operator!  The defined operators 
in this upload are S^, S@, S!, and STYPE.  WARNING, if you forget to 
terminate a structure, no structure reference will be compiled and an 
error message MAY NOT be given.  Remember also that field names ARE CASE 
SENSITIVE and LOCAL to the structure template.  Last comment, structures 
MAY be nested to any level. ) 

fp

template Point
 :word x
 :word y
tend

template Rect
  :word top
  :word left
  :word bottom
  :word right
tend  ( TEND ends template definition )
  
\ example for FP parameters 
template parameter
30 :string name
 :real value
30 :string unit
tend

template measurement
 :long date \ in internal Mac format
80 :string title
255 :string descriptor
parameter :struct wavelength
parameter :struct temperature
parameter :struct angle
256:array time
256:array counts
tend

measurement structure curve1

: testarray
 100 0 do i 4* i curve1 time s! loop
 100 0 do i curve1 time s@ . cr loop;

: .date ( DateTime DateForm ) { | [ 40 lallot ] mydate -- }
 8 shift ^ mydate call IUDateString ^ mydate count type;

: read.int
 begin
 pad 1+ 80 expect span @ pad c! pad number? not while
 drop cr .” Illegal number [integer], reenter - “
 repeat;

: read.float
 begin
 pad 1+ 80 expect span @ pad c! pad fnumber? not while
 fdrop cr .” Illegal number [float], reenter - “
 repeat;

: setup.curve1 { | dattim -- }
 ^ dattim call readdatetime drop @
 cr .” Today is “ 1 .date
 cr .” Setting up parameters for curve 1.”
 dattim curve1 date s!
 “ lambda” dup c@ 1+ curve1 wavelength name s^ swap cmove 
 “      T” dup c@ 1+ curve1 temperature name s^ swap cmove 
 “  delta” dup c@ 1+ curve1 angle name s^ swap cmove 
 “ [nm]” dup c@ 1+ curve1 wavelength unit s^ swap cmove 
 “  [K]” dup c@ 1+ curve1 temperature unit s^ swap cmove 
 “  [°]” dup c@ 1+ curve1 angle unit s^ swap cmove 
 cr .” Title (one line) - “ cr pad 80 expect
 span @ curve1 title s^ c!
 pad curve1 title s^ 1+ span @ cmove 
 cr .” Description (one line) - “ cr pad 80 expect
 span @ curve1 descriptor s^ c!
 pad curve1 descriptor s^ 1+ span @ cmove
 cr .” lambda [nm] - “ read.float curve1 wavelength value s!
 cr .”      T  [K] - “ read.float curve1 temperature value s!
 cr .”  delta  [°] - “ read.float curve1 angle value s!
\ example setup of ‘measurement data’
 20 0 do
 i i curve1 time s!
 i 100 * i curve1 counts s!
 loop

 cr .” End setup -- “ cr;
 
: dump.curve1 { | [ 80 lallot ] mydate -- }
 cr .” Data taken on “ curve1 date s@ 1 .date
 cr curve1 title s^ count type
 cr curve1 descriptor s^ count type
 cr curve1 wavelength name s^ count type .” = “
 curve1 wavelength value s@ f.
 curve1 wavelength unit s^ count type
 cr curve1 temperature name s^ count type .” = “
 curve1 temperature value s@ f.
 curve1 temperature unit s^ count type
 cr curve1 angle name s^ count type .” = “
 curve1 angle value s@ f.
 curve1 angle unit s^ count type
 cr .” data follows:”
 20 0 do cr
 i curve1 time s@ . space
 i curve1 counts s@ .
 loop
 cr
;
Listing 2: Structure definitions from ECFB
\ downloaded from GEnie  J. L. Nov 1988
\ Originally from East Coast Forth Board, 
\ author A. Nonymous
( This is a set of machforth routines for building records. They allow 
you to build a named record with items of various sizes. Executing the 
record name leaves the record size on the stack, executing an item name 
leaves the offset of the item into the record on the stack. It creates 
a template for the record but not the actual record. Create the record 
with “ create <name> <record name> allot” or “variable <name> <record 
name> 4 - vallot” depending if you want the entry in the dictionary or 
variable space )
 
VOCABULARY RECORDS ( NEW VOCABULARY )
ALSO RECORDS
DEFINITIONS
 
Global
: Align ( n1 -- [n1] or [n1 + 1] makes n word aligned )
        dup 2 mod + ; ( USED TO WORD ALIGN 2 & 4 BYTE ITEMS )
 
Global
: RECORD ( -- a 0)
         HERE 4 +  CREATE  0 dup W,  DOES>  W@ ;
         ( USED TO OPEN A RECORD )
 
Global
: BYTE: ( a n -- a n1+1)
        CREATE DUP W, 1+ DOES> W@ + ;
 
Global
: BYTES: ( a n1 n2 -- a n1+n2 | AN ARRAY OF n2 bytes ) 
        CREATE OVER Align W, swap Align + DOES> W@ + ;
 
Global
: SHORT: ( a n1  -- a n1+2 | 2 byte integer item )
        CREATE Align DUP W, 2+ DOES> W@ + ;

Global
: WORD: ( a n1  -- a n1+2 | 2 byte integer item )
        CREATE Align DUP W, 2+ DOES> W@ + ;
 
Global
: BOOLEAN: ( a n1  -- a n1+2 | 2 byte boolean item )
        CREATE Align DUP W, 2+ DOES> W@ + ;
Global
: SHORTS: ( a n1 n2 -- a n1+n2*2 | an array of n2 shorts )
   CREATE OVER Align  W,  2* Swap Align  + DOES> W@ + ;
 
Global
: LONG:  ( a n1  -- a n1+4 | a 4 byte integer )
        CREATE Align DUP W, 4 + DOES> W@ + ;
Global
: POINTER:  ( a n1  -- a n1+4 | a 4 byte integer )
        CREATE Align DUP W, 4 + DOES> W@ + ;
Global
: LONGS: 
 ( a n1 n2 -- a n1+n2*4 | an array of n2 4 byte integers )
  CREATE OVER Align  W, 4 * swap Align + DOES> W@ + ;
Global
: HANDLE: ( a n1  -- a n1+4 | a handle, 4 byte, item )
        CREATE Align DUP W, 4 + DOES> W@ + ;
Global
: HANDLES: ( a n1 n2 -- a n1+n2*4| array of n2 handles )
  CREATE OVER Align  W, 4 * swap Align  + DOES> W@ + ;
 
Global
: ADDR: ( a n1  -- a n1+4 | 4 byte address item, ie pointer )
        CREATE Align DUP W, 4 + DOES> W@ + ;
Global
: ADDRS: ( a n1 n2 -- a n1+n2*4 | array of n2 addresses )
  CREATE OVER Align  W, 4 * swap Align + DOES> W@ + ;
Global
: RECT: ( a n1 n2 -- a n1+8 | a rect item )
  CREATE Align DUP W, 8 + DOES> W@ + ;
Global
: RECTS: ( a n1 n2 -- a n1+n2*8 | an array of n2 rects )
  CREATE  OVER Align  W, 8 * swap Align + DOES> W@ + ;
Global
: STRING: ( a n1 n2 -- a n1+n2+1 | a string item n2+1 long ) 
  CREATE OVER W, + 1+ DOES> W@ + ;
Global
: RECORD: ( a n1 n2 -- a n1+n2 | a record item of size n2) 
  CREATE OVER Align  W, swap Align + DOES> W@ + ;
Global
: END.RECORD 
 { Mainaddr size --|sets size of struct at a to n }
                Mainaddr W@ Size <
                IF Size MainAddr W! THen ;
          ( CLOSES RECORD, STORES RECORD SIZE IN RECORD NAME)
Global
: SUB.REC ( -- )
        CReate  0 W, 2DUP Here 2- Rot Rot DOES> W@ ;
        ( USE TO CREATE VARIANT RECORD ON THE END OF A RECORD)
Global
: END.SUB { SubAddrs MainAddrs Size -- }
        Size SubAddrs W!
        MainAddrs W@ Size <
        IF Size Align MainAddrs W! THen ;
        ( USE TO CLOSE VARIANT RECORD ) 
 
ONLY MAC
ALSO FORTH
DEFINITIONS
ALSO RECORDS
 
Global
RECORD Rectangle
        Global  SHORT: Top
        Global  Short: Left
        Global  Short: Bottom
        Global  SHORT: RIght
ENd.RECORD
 
Global
: rect Variable Rectangle 4 - VALLOT ;
( CREATES A RECTANGLE RECORD IN THE VARIABLE SPACE )

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

VOX 2.8.6 - Music player that supports m...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more
MacUpdate Desktop 6.1.3 - Search and ins...
MacUpdate Desktop 6 brings seamless 1-click app installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on... Read more
ExpanDrive 5.4.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Espionage 3.6.6 - Simple, state-of-the-a...
Espionage offers state-of-the-art encryption and plausible deniability for your confidential data. Sometimes, encrypting your data isn't enough to protect it. That's why Espionage 3 goes beyond data... Read more
Pinegrow Web Designer 2.94 - Mockup and...
Pinegrow Web Designer is desktop app that lets you mockup and design webpages faster with multi-page editing, CSS and LESS styling, and smart components for Bootstrap, Foundation, Angular JS, and... Read more
1Password 6.3.3 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Sublime Text 3126 - Sophisticated text e...
Sublime Text is a sophisticated text editor for code, markup, and prose. You'll love the slick user interface, extraordinary features, and amazing performance. Features Goto Anything. Use Goto... Read more
ForkLift 3.0 Beta 2 - Powerful file mana...
ForkLift is a powerful file manager and ferociously fast FTP client clothed in a clean and versatile UI that offers the combination of absolute simplicity and raw power expected from a well-executed... Read more
OmniFocus 2.7.1 - GTD task manager with...
OmniFocus helps you manage your tasks the way that you want, freeing you to focus your attention on the things that matter to you most. Capturing tasks and ideas is always a keyboard shortcut away in... Read more
CleanApp 5.1.1 - Application deinstaller...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more

Concrete Jungle (Games)
Concrete Jungle 1.16 Device: iOS Universal Category: Games Price: $4.99, Version: 1.16 (iTunes) Description: A follow up to the puzzle hit 'MegaCity'! Concrete Jungle is a new take on the city building genre that swaps micro-... | Read more »
5 great apps for the budget traveller
Travelling abroad, or even within your home country, has never been easier thanks to our handy smartphone companions. There are hundreds of apps on the market that promise to make your world journeys hassle-free, but we've selected five of the... | Read more »
Zip—Zap (Games)
Zip—Zap 1.01 Device: iOS Universal Category: Games Price: $1.99, Version: 1.01 (iTunes) Description: Touch to contract.Release to let go.Bring the clumsy mechanical beings home. · · · over 100 levelsno adsno in-app-purchases Zip—... | Read more »
Paperback: The Game (Games)
Paperback: The Game 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: You are an author trying to finish kitschy paperback novels. Complete Westerns, Science Fiction, Romance or even a Crime... | Read more »
How to Rule With a Firm Hand in My Majes...
My Majesty is a kingdom management sim not unlike August’s magisterial hit, Reigns. It’s essentially a reskin of developer Tigrido’s previous management sim, Dictator. As supreme ruler of the land, you must consult with a number of subjects to... | Read more »
Our 5 Favorite iMessage Sticker Packs
At long last, iMessage joins the ranks of messaging apps the likes of LINE and Whatsapp, adding an impressive collection of stickers. They’re a great way to add a little something extra to your daily conversations. [Read more] | Read more »
How to get past Vulture Island's tr...
Vulture Island is a colorful and quirky mish-mash of platforming and puzzles. It’s creative and fresh, but sometimes the game can throw a curveball at you, leaving you stuck as to how you should progress. These tips will help you explore smoothly... | Read more »
The new Clash of Kings is just for Weste...
If you’ve played the original Clash of Kings, you’ll probably recognise the city building, alliance forging and strategic battles in Clash of Kings: The West. What sets this version apart is that it’s tailor made for a Western audience and the... | Read more »
Frost - Survival card game (Games)
Frost - Survival card game 1.12.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.12.1 (iTunes) Description: *Warning: the game will work on iPhone 5C and above and iPad Pro / 4. Other devices are not supported* | Read more »
How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »

Price Scanner via MacPrices.net

Apple refurbished Mac minis available startin...
Apple has Certified Refurbished Mac minis available starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: - 1.4GHz Mac mini: $419 $80 off MSRP - 2.6GHz Mac... Read more
13-inch 2.5GHz MacBook Pro available for $928...
Overstock has the 13″ 2.5GHz MacBook Pro available for $927.99 including free shipping. Their price is $171 off MSRP. Read more
Buying McLaren Would Give Apple Instant Car C...
Apple “iCar” rumors have waxed and waned over the years, piquing interest and speculation as to whether Apple is seriously interested in getting into the automotobile business, either in a joint... Read more
Aetna to Transform Members’ Consumer Health E...
Health care benefits company Aetna, which has an estimated 46.3 million clients, today announced a new initiative to revolutionize members consumer health experience by combining the power of iOS... Read more
USB-IF Announces USB Audio Device Class 3.0 S...
USB Implementers Forum (USB-IF), the support organization for the advancement and adoption of USB technology, today announced the USB Audio Device Class 3.0 specification to establish USB Audio over... Read more
Clearance 12-inch 1.2GHz Retina MacBooks, App...
Apple has Certified Refurbished 2015 12″ 1.2GHz Retina MacBooks available for $1189, or $410 off original MSRP. Apple will include a standard one-year warranty with each MacBook, and shipping is free... Read more
Logitech SmartDock and Skype For Business Com...
Logitech has announced Logitech SmartDock, an AV meeting room solution designed in collaboration with Microsoft. Logitech SmartDock works with Skype for Business and qualified devices, including... Read more
27-inch iMacs on sale for up to $220 off MSRP
B&H Photo has 27″ Apple iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2099 $200 off MSRP - 27″ 3.2GHz/1TB Fusion iMac 5K: $1899.99 $... Read more
Apple Macs and iPads available for up to $300...
Purchase a new Mac or iPad using Apple’s Education Store and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free, and... Read more
Save up to $600 with Apple refurbished Mac Pr...
Apple has Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The following... Read more

Jobs Board

Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
Sr. *Apple* Mac Engineer - Net2Source Inc....
…staffing, training and technology. We have following position open with our client. Sr. Apple Mac Engineer6+ Months CTH Start date : 19th Sept Travelling Job If Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.