`8NNNNAQp NNNuTHIS DISK HAS BEEN IMMUNIZED AGAINST MOST VIRUSES BY THE 'ULTIMATE VIRUS KILLER' VERSION 6.0 BY RICHARD KARSMAKERS, *THE* ATARI VIRUS KILLER!!!puke'(*** `  @!#@%`')+-1 3@o79;OE`KMQ S@U`WY[]_a c@e`gikmoq s@u`wy{O ` o @ @ ` ׀ ٠  @` @`!Aa   !Aa!!#A%a')+-/1!3A5a79;=?A!CAEaGIKMOQ!SAUaWY[]_a!cAeagikmoquawy{}!Aa!Aa!Ao!Aa!Aaǁɡ!Aaׁ١!A!Aa " B b  !"!B!b!!!!!"!""#B"%b"'")"+"-"/#1"#3B#5b#7#9#=#?$A"$CB$Eb$G$I$K$M$O%Q"%SB%Ub%W%Y%[%_&a"&cB&eb&g&i&k&m&o'q"'sB'o'w'y'{'}'("(B(b((((()")B)b)))))*****`  @!#@%`')+-1 3@o79;OE`KMQ S@U`WY[]_a c@e`gikmoq s@u`wy{O ` o @ @ ` ׀ ٠  @` @`!Aa   !Aa!!#A%a')+-/1!3A5a79;=?A!CAEaGIKMOQ!SAUaWY[]_a!cAeagikmoquawy{}!Aa!Aa!Ao!Aa!Aaǁɡ!Aaׁ١!A!Aa " B b  !"!B!b!!!!!"!""#B"%b"'")"+"-"/#1"#3B#5b#7#9#=#?$A"$CB$Eb$G$I$K$M$O%Q"%SB%Ub%W%Y%[%_&a"&cB&eb&g&i&k&m&o'q"'sB'o'w'y'{'}'("(B(b((((()")B)b)))))*****ICTARI 021ASSEMBLY eAUTO eC eGFA  frMISC "fSTOS FfDESKTOP INF ICTARI TXT hOKREAD_ME TXT Mq}W . e.. MISCMATH eRSC_CODE eRSCDEMO e<VERSCAN eOJOY_PAD e. e.. eMATHS S U@* From: Jim Taylor, Sunderland. * In reponse to Tony Harris's request for sine wave generation in * assembler here are a few miscellaneous maths routines I have * gleaned/written/poached/hacked over the years. * The trig. routines are towards the end. *=============================================================== * dascth Convert a signed, null terminated ascii * " string to a 32 bit hex value. * dascth [#]str_addr,result.l dascth macro [#]addr_in,result.l move.l \1,-44(sp) jsr dascth move.l -44(sp),\2 endm * Enter here with a0 point_ing to 1st byte. * Exit with hex answer in d0.l dascth movem.l d0-d4/a0-a3,-(sp) move.l -4(sp),a0 * Address of ascii string clr.l d0 * Clear answer reg. clr.l d1 * Init. counter move.l #adder,a1 * Init adder address .get_null tst.b (a0) * Null? beq.s .convert * Yes adda.l #1,a0 * Adv to next char addq #1,d1 * Inc counter bra.s .get_null .convert dbra d1,.get_byte bra .done .get_byte clr.l d3 * Init scratchreg move.b -(a0),d3 * Get an ascii char cmpi.b #'-',d3 * Negative? beq.s .neg subi #'0',d3 * Convert to dec move.l (a1)+,d2 * Get adder clr.l d4 * Init sub result * Multiply adder by digit .nxt dbra d3,.add bra .updat .add add.l d2,d4 bra .nxt * Add on product .updat add.l d4,d0 * Update answer bra.s .convert .neg neg.l d0 .done move.l d0,-4(sp) movem.l (sp)+,d0-d4/a0-a3 rts adder dc.l 1 dc.l 10 dc.l 100 dc.l 1000 dc.l 10000 dc.l 100000 dc.l 1000000 dc.l 10000000 dc.l 100000000 dc.l 1000000000 dc.l 10000000000 *=============================================================== * hex_to_ascii Convert a hex value to a null terminated * " ascii string. [OBSOLETE] * eg. hex_to_ascii d1,ascnum hex_to_ascii macro source_of_data.l,destination_address move.l \1,d0 lea \2,a0 jsr hex_to_ascii endm hex_to_ascii * Enter with hex number in d0.l. Exit with dec number in d0.l jsr hex_to_dec * Enter with dec number in d0.l and destination address of ascii * characters in a0 jsr dec_to_ascii rts *=============================================================== * hexasc Convert a hex value in d0 to an ascii * " string in ASCNUM. * Enter with hex number in d0. Exit with 1st ascii char in ASCNUM and * number of characters in d0. Uses d1,d2,d3 & a0 hexasc jsr hex_to_dec jsr decasc rts *=============================================================== * hex_to_dec Convert a hex value to decimal * Enter with hex number in d0.l. Return with dec number in d0. Uses d1 * & d2 hex_to_dec movem.l d1-d2,-(sp) clr.l d1 * Clear the answer register move.l #1000,d2 * Init multiplier .hex3 cmp.l d0,d2 * Is multiplier not too big ble .hex1 * Apparently cmpi #1,d2 * Multiplier = 1? (ie. finished) beq .hex2 * Yes asl.l #4,d1 divu #10,d2 bra .hex3 .hex1 sub.l d2,d0 cmpi.b #9,d1 bne .hex4 addq.b #6,d1 * 9+6=$f: $f+1=$10 .hex4 addq.b #1,d1 bra .hex3 .hex2 move.l d1,d0 movem.l (sp)+,d1-d2 rts *=============================================================== * hex_to_asc Convert 16bit value to 4 ascii hex digits * From page 11-6 of: * '68000 Assembly Language Programming' hex_to_asc move.l #ascnum,-(sp) move d0,-(sp) jsr binhex rts binhex movem.l d0-d2/a0,-(sp) moveq #3,d1 * Loop ctr 4-1 move 16+4(sp),d2 * Get value move.l 16+6(sp),a0 * Get string addr adda.l #4,a0 * Adjust pointer past end of string .loop move.b d2,d0 andi.b #$f,d0 jsr hexdigit move.b d0,-(a0) * Save ascii digit lsr.w #4,d2 * Shift d2 to get next nibble dbra d1,.loop movem.l (sp)+,d0-d2/a0 move.l (sp),6(sp) * Move return addr down adda.l #6,sp * Adjust stack pointer to ret addr rts hexdigit cmp.b #$a,d0 * Decimal digit or hex letter? blt.s .addz add.b #'A'-'0'-$a,d0 * Offset for letters .addz add.b #'0',d0 * Convert to ascii rts *=============================================================== * decasc Convert a decimal value to an ascii string. * Enter with dec value in d0. * Exit with 1st ascii char in ASCNUM(0) * and number of characters in d0. Uses d1,d2,d3 & a0 decasc movem.l d1-d3/d7/a0,-(sp) move.l #ascnum,a0 * Get buffer address move.l #7,d2 * Set digit counter move.l #1,d3 * Init ascii char counter move.l #$30,d1 .dec1 rol.l #4,d0 move.l d0,d7 * Save value andi.l #$f,d0 * Isolate digit cmpi.b #0,d0 bne .dec2 move.l d7,d0 * Retrieve value dbra d2,.dec1 * Skip leading zero's move.b d1,(a0)+ * Store ascii zero bra .dec4 * Return .dec3 rol.l #4,d0 * Get next digit move.l d0,d7 * Save value andi.l #$f,d0 * Isolate digit addq.l #1,d3 * Inc. ascii character counter move.l #$30,d1 .dec2 add.b d0,d1 * Conv. to ascii in d1 move.b d1,(a0)+ * Store ascii character move.l d7,d0 * Retrieve value dbra d2,.dec3 .dec4 move.l d3,d0 * Get ascii count movem.l (sp)+,a0/d7/d1-d3 rts *=============================================================== * dec_to_ascii Convert a decimal value to an ascii string. * Enter with decimal value in d0.l * Exit with ascii characters in ASCNUM dec_to_ascii movem.l d0-d2/a0,-(sp) move #7,d1 * Counter clr d2 .nulls rol.l #4,d0 * Move to next dec character move d0,d2 * Get a character andi #%1111,d2 * Isolate it tst.b d2 * Null? bne .chars * No dbra d1,.nulls clr d1 * Answer is zero, so process it .chars clr d2 move.b d0,d2 * Get a character andi #%1111,d2 * Isolate it addi #$30,d2 * Convert to ascii move.b d2,(a0)+ * Store in memory rol.l #4,d0 * Advance next character dbra d1,.chars clr.b (a0) * Null terminator movem.l (sp)+,d0-d2/a0 rts *=============================================================== * aschex Convert dec. str to hex. value. * Enter here with a0 point_ing 1 byte past last ascii byte. * Exit with hex answer in d0 asch8 movem.l d1-d3,-(sp) bra.s asch5 * Enter here with a0 point_ing to ascii byte length in buffer. * Exit with hex answer in d0 aschex movem.l d1-d3,-(sp) clr.l d3 move.b (a0),d3 * String length in d3 addi #1,d3 * Inc string length add d3,a0 * Go to lsb subq #2,d3 * Adjust counter for dbra asch5 clr.l d0 * Clear answer register .asch4 move.l #1,d1 * Initialise counter .asch3 move.b -(a0),d2 * Get a digit cmpi.b #'-',d2 * Is it a minus sign bne .asch7 neg d0 convert to negative .asch7 cmpi.b #',',d2 = ',' bne .asch2 swap d0 put number into hi-bytes bra .asch4 .asch2 cmpi.b #$39,d2 d2>'9' (ie. all numbers done) bgt .asch6 cmpi.b #$30,d2 d2<'0' (ie. all numbers done) blt .asch6 cmpi.b #$31,d2 less than 1 ? blt .asch1 down to zero ? add d1,d0 no. inc answer sub #1,d2 dec digit bra .asch2 .asch1 mulu #10,d1 inc counter dbra d3,.asch3 .asch6 movem.l (sp)+,d1-d3 rts ascdec clr.l d1 clr.l d0 move.l #wrtbuff,a1 .ascd2 move.b (a1)+,d1 cmpi.b #'.',d1 beq .ascd3 subi.b #48,d1 .ascd1 asl #4,d0 add.l d1,d0 bra .ascd2 .ascd3 rts *=============================================================== * sqrt32 Calculates the square root of a * " 32 bit integer in d0. * This routine calculates the square root of a 32 bit integer in d0 * max value 65536*200=13107200. * Returns the square root as a 16 bit integer in the low word of d1. * The original number in d0 is not affected. sqrt32 movem.l d2-d3,-(sp) save scratch registers move.l d0,d2 copy data value to d2 divu #200,d2 divide by 200 addq #2,d2 then add 2 .nxtapp move.l d0,d1 load data value value into d1 divu d2,d1 divide it by last approx. move d1,d3 and put new approx in d3 sub d2,d3 last two approx's identical ? beq.s .done yes. exit cmpi #1,d3 no. check for done. beq.s .done cmpi #-1,d3 beq.s .done add d1,d2 add last two approx's lsr #1,d2 and divide sum by two bra.s .nxtapp .done movem.l (sp)+,d2-d3 rts *=============================================================== * sincosa Sine & cosine function * sincosa angle*10,sine*16384,cos*16384 sincosa macro angle*10,sin,cos move \1,-32(sp) jsr sincosa move -32(sp),\2 move -34(sp),\3 endm * sine and cosine Function, angle is passed in d0 and * the sine and cosine are returned in d1 and d2 sincosa movem.l d0-d4/a0,-(sp) move -4(sp),d0 * Retrieve angle from stack tst d0 * Angle negative, add 360 degrees bpl .noaddi add #3600,d0 .noaddi lea sintab,a0 * Beginning address of sine table andi.l #$ffff,d0 divu #10,d0 * Convert to angle swap d0 move d0,d3 * Get remainder swap d0 lsl #1,d0 * Angle times two as index for access move 0(a0,d0),d1 * sine to d1 move 2(a0,d0),d4 * Next sine to d4 sub d1,d4 * Interpolate dec amount muls d3,d4 * base+(next-base)*(rem/10) divs #10,d4 add d4,d1 cmp #270*2,d0 * Calculate cosine through blt .plus9 * displacement of sine values sub #270*2,d0 * by 90 degrees bra .sendsin .plus9 add #90*2,d0 .sendsin move 0(a0,d0),d2 * cosine to d2 move 2(a0,d0),d4 * Next cosine to d4 sub d2,d4 * Interpolate dec amount muls d3,d4 * base+(next-base)*(rem/10) divs #10,d4 add d4,d2 move d1,-4(sp) move d2,-6(sp) movem.l (sp)+,d0-d4/a0 rts *=============================================================== * sincos Sine & cosine function * sincos angle,sine,cos sincos macro angle,sin,cos move \1,-22(sp) jsr sincos move -22(sp),\2 move -24(sp),\3 endm * sine and cosine Function, angle is passed in d0 and * the sine and cosine are returned in d1 and d2 sincos movem.l d0-d2/a0,-(sp) move -2(sp),d0 * Retrieve angle from stack tst d0 * Angle negative, add 360 degrees bpl noaddi add #360,d0 noaddi lea sintab,a0 * Beginning address of sine table move.l d0,d2 * Angle in d0 and d2 lsl #1,d0 * Angle times two as index for access move 0(a0,d0),d1 * sine to d1 cmp #270,d2 * Calculate cosine through blt plus9 * displacement of sine values sub #270,d2 * by 90 degrees bra sendsin plus9 add #90,d2 sendsin lsl #1,d2 move 0(a0,d2),d2 * cosine to d2 move d1,-2(sp) move d2,-4(sp) movem.l (sp)+,d0-d2/a0 rts *=============================================================== * sin Sine function (d0=angle) * jsr sin * sine function * Angle is passed in d0 and the sine returned in d1 sin lea sintab,a1 tst d0 bpl sin1 add #360,d0 sin1 lsl #1,d0 move 0(a1,d0),d1 rts * sintab Sine table for above sincos & sin section data sintab _0deg dc.w 00000,00286,00572,00857,01143,01428,01713,01997,02280 dc.w 02563 _10deg dc.w 02845,03126,03406,03686,03964,04240,04516,04790,05063 dc.w 05334 _20deg dc.w 05604,05872,06138,06402,06664,06924,07182,07438,07692 dc.w 07943 _30deg dc.w 08192,08438,08682,08923,09162,09397,09630,09860,10087 dc.w 10311 _40deg dc.w 10531,10749,10963,11174,11381,11585,11786,11982,12176 dc.w 12365 _50deg dc.w 12551,12733,12911,13085,13255,13421,13583,13741,13894 dc.w 14044 dc.w 14189,14330,14466,14598,14726,14849,14962,15082,15191 dc.w 15296 dc.w 15396,15491,15582,15668,15749,15826,15897,15964,16026 dc.w 16083 dc.w 16135,16182,16225,16262,16294,16322,16344,16362,16374 dc.w 16382 _90deg dc.w 16384,16382,16374,16362,16344,16322,16294,16262,16225,16182 dc.w 16135,16083,16026,15964,15897,15826,15749,15668,15582,15491 dc.w 15396,15296,15191,15082,14962,14849,14726,14598,14466,14330 dc.w 14189,14044,13894,13741,13583,13421,13255,13085,12911,12733 dc.w 12551,12365,12176,11982,11786,11585,11381,11174,10963,10749 dc.w 10531,10311,10087,09860,09630,09397,09162,08923,08682,08438 dc.w 08192,07943,07692,07438,07182,06924,06664,06402,06138,05872 dc.w 05604,05334,05063,04790,04516,04240,03964,03686,03406,03126 dc.w 02845,02563,02280,01997,01713,01428,01143,00857,00572,00286 _180deg dc.w 00000,-0286,-0572,-0857,-1143,-1428,-1713,-1997,-2280,-2563 dc.w -2845,-3126,-3406,-3686,-3964,-4240,-4516,-4790,-5063,-5334 dc.w -5604,-5872,-6138,-6402,-6664,-6924,-7182,-7438,-7692,-7943 dc.w -08192,-08438,-08682,-08923,-09162,-09397,-09630,-09860,-10087,-10311 dc.w -10531,-10749,-10963,-11174,-11381,-11585,-11786,-11982,-12176,-12365 dc.w -12551,-12733,-12911,-13085,-13255,-13421,-13583,-13741,-13894,-14044 dc.w -14189,-14330,-14466,-14598,-14726,-14849,-14962,-15082,-15191,-15296 dc.w -15396,-15491,-15582,-15668,-15749,-15826,-15897,-15964,-16026,-16083 dc.w -16135,-16182,-16225,-16262,-16294,-16322,-16344,-16362,-16374,-16382 _270deg dc.w -16384,-16382,-16374,-16362,-16344,-16322,-16294,-16262,-16225,-16182 dc.w -16135,-16083,-16026,-15964,-15897,-15826,-15749,-15668,-15582,-15491 dc.w -15396,-15296,-15191,-15082,-14962,-14849,-14726,-14598,-14466,-14330 dc.w -14189,-14044,-13894,-13741,-13583,-13421,-13255,-13085,-12911,-12733 dc.w -12551,-12365,-12176,-11982,-11786,-11585,-11381,-11174,-10963,-10749 dc.w -10531,-10311,-10087,-09860,-09630,-09397,-09162,-08923,-08682,-08438 dc.w -8192,-7943,-7692,-7438,-7182,-6924,-6664,-6402,-6138,-5872 dc.w -5604,-5334,-5063,-4790,-4516,-4240,-3964,-3686,-3406,-3126 dc.w -2845,-2563,-2280,-1997,-1713,-1428,-1143,-0857,-0572,-0286 dc.w 0,286 section text *=============================================================== * angle Return angle from X & Y (d0.w=X d1.w=Y) * jsr angle * Return the angle generated by X & Y coords * In: d0.w - X coord d1.w - Y coord * Out: angle.w - _angle angle movem.l d0-d6/a0-a2,-(sp) move d0,d2 * Copy Y bpl ang1 neg d2 ang1 move d1,d3 * Copy X bpl ang2 neg d3 ang2 cmp d3,d2 * Y:X bge ang3 exg d2,d3 * X<->Y jsr det_ang neg d4 addi #90,d4 bra ang4 ang3 jsr det_ang * Convert to full circle ang4 tst d0 * tst X bmi ang5 tst d1 * tst Y bpl ang7 neg d4 addi #360,d4 bra ang7 ang5 tst d1 bmi ang6 neg d4 ang6 addi #180,d4 ang7 cmp #360,d4 bne ang8 clr d4 ang8 move d4,_angle movem.l (sp)+,d0-d6/a0-a2 rts * Determine angle det_ang mulu #32768,d3 tst d2 bne da2 clr d4 bra da3 da2 divu d2,d3 * 65535*Y/X lea tantab,a0 da1 cmp (a0)+,d3 bhi da1 move.l a0,d4 sub.l #tantab,d4 asr d4 * Angle subq #1,d4 da3 rts * tantab Tangent table for above angle section data tantab dc.w 00286,00858,01431,02004,02579,03155,03733,04314,04897 dc.w 05484 dc.w 06073,06667,07265,07867,08474,09087,09706,10332,10964 dc.w 11604 dc.w 12252,12908,13753,14248,14933,15630,16338,17058,17792 dc.w 18539 dc.w 19302,12908,20876,21689,22521,23373,24247,25144,26065 dc.w 27012 dc.w 27987,28991,30026,31096,32201,33345 section text *=============================================================== * sine Computed sine function * In:d0=angle Out:d1=2048*sine * sine angle,sine sine macro angle,sine move.l \1,d0 jsr sine move.l d1,\2 endm sine_b4 equ $6c68 sine_b2 equ -$767d61b6 sine_b1 equ $23be8d44 sine_rnd equ $8000 * Computed sine function. See ST World p78 issue 31 sine movem.l d2-d3,-(sp) move.l #16,d3 * Shift count move.l d0,d1 * Copy angle (x) muls d1,d1 * x^2 move.l d1,d2 * Copy it muls #sine_b4,d2 * (2^51)*b4*(x^2) addi.l #sine_b2,d2 * (2^51)*[b2+b4*(x^2)] addi.l #sine_rnd,d2 * Round off (try removing this) asr.l d3,d2 * (2^35)*[b2+b4*(x^2)] muls d2,d1 * (2^35)*(x^2)*[b2+b4*(x^2)] addi.l #sine_b1,d1 * (2^35)*{b1+(x^2)*[b2+b4*(x^2)]} addi.l #sine_rnd,d1 * Round off (try removing this) asr.l d3,d1 * (2^19)*{b1+(x^2)*[b2+b4*(x^2)]} muls d0,d1 * (2^19)*sin(x) asr.l #8,d1 * 2048*sin(x) movem.l (sp)+,d2-d3 rts *=============================================================== * cosine Computed cosine function * In:d0=angle Out:d1=2048*cosine * cosine angle,cosine cosine macro angle,cosine move.l \1,d0 jsr cosine move.l d1,\2 endm cos_b4 equ $7620 cos_b2 equ -$4f539bb8 cos_b1 equ $8000000 cos_rnd equ $8000 * Computed cosine function. See ST World p79 issue 31 cosine movem.l d2-d3,-(sp) move.l #16,d3 move.l d0,d1 * Copy angle (x) muls d1,d1 * x^2 move.l d1,d2 * Copy it muls #cos_b4,d2 * (2^43)*b4*(x^2) addi.l #cos_b2,d2 * (2^43)*[b2+b4*(x^2)] addi.l #cos_rnd,d2 * Round off (try removing this) asr.l d3,d2 * (2^27)*[b2+b4*(x^2)] muls d2,d1 * (2^27)*(x^2)*[b2+b4*(x^2)] addi.l #cos_b1,d1 * (2^27)*{1+(x^2)*[b2+b4*(x^2)]} asr.l d3,d1 * (2^11)*cosine(x) movem.l (sp)+,d2-d3 rts *========================= End of File ================================ . e.. eRSCDEFS DOC cf.RSCDEFS S p"0RSCINLINDOC Ucf/lRSCINLINS նY5 RSCDEFS ~~~~~~~~~~~~~ by: Mrten Lindstrm The file RSCDEFS.S contains DEVPAC3 assembler macros to simplify the task of creating object trees and whole resources directly in the source text. The parameters: --------------- name: Any name, but must be the same in an object and its related secondary structures. You can use this name in your program to refer to a tree or object. See more below. templ,valstr: (in TEDINFOs) should be names used with STRings, or 'null' mask: (in ICONBLKs) should be a name used with an IMG flags: NOFLAG, SELECTAB, DEFAULT, EXIT, TOUCHXIT, EDITABLE, RADIO, HIDETREE, INDIRECT or a combination thereof (e.g. SELECTAB+DEFAULT) (The LAST flag is taken care of implicitly by the macros) state: NOSTATE, SELECTED, CROSSED, CHECKED, DISABLED, OUTLINED, SHADOWED or a combination thereof (e.g. SHADOWED+SELECTED) xxxcol: WHITE, BLACK, RED, GREEN, BLUE, CYAN, YELLOW, MAGENTA, LWHITE, LBLACK, LRED, LGREEN, LBLUE, LCYAN, LYELLOW, LMAGENTA fill: any one colour + one of FILL0, FILL1, FILL2, FILL3, FILL4, FILL5, FILL6, FILL7 txtbg: OPAQUE, TRANSPAR font: (in TEDINFOs) SYSFONT, MINIFONT justif: (in TEDINFOs) LEFT, RIGHT, CENTRE NOTE: Instead of NOFLAG or NOSTATE a null string could be used, i.e. no character or space used between the two commas separating the 'flags' or 'state' parameter respectively. Name: ----- The name used as parameter in various structures should be the same in all related structures. E.g. if a TEXT OBJECT is given a certain name, the same name must be used for a related TEDINFO and for a STRing as well. (A 'tree name' is simply the name of the root object of the tree.) The name is used thus by the macros: literal name: For object except root: symbol for the OBJECT NUMBER For root in rsc: SYMBOL for TREE NUMBER For root outside rsc: LABEL for TREE ADDRESS For free string/image: symbol for its number In addition it is used in various combinations, for internal use, which should in most cases be of less interest to the macro user: s+name: label for string l+name: symbol for string length including null i+name: label for image b+name: label for BITBLK/ICONBLK t+name: label for TEDINFO a+name: label for APPLBLK y+name: label for last child object z+name: label for end of last child's family (=next sibling object?) BEG_RSC, END_RSC ---------------- The macros can be used either to create a full resource, in standard RSC file format, or to create the odd object tree. A full resource should be begun with BEG_RSC and ended with END_RSC. Furthermore each type of structure most come in a certain order: 1) STRings 2) IMGs 3) BITBLKs 4) ICONBLKs 5) TEDINFOs 6) objects (trees) 7) FREESTRs (free strings) 8) FREEIMGs (free images) (Not all types have to be used of course) If you create object trees and other structures outside of an RSC (i.e. before an RSC_BEG or after RSC_END) you can ignore these rules. NOTE that names of trees outside of an RSC should be used, in your program, as labels for the tree addresses, while names of trees inside an RSC are equated to the tree numbers. FAMILBEG, FAMILAST, FAMILEND and TREE ------------------------------------- For each type of object there is one macro, but to combine them into a tree structure some further macros are needed. 'FAMILBEG name', must precede any parent object except a root 'FAMILAST name', must precede its last child (the name is the parent's) 'FAMILEND name', must normally follow the last child 'TREE name', must precede a root object (This isn't as neat as I would have wished it to be. It would have been better if only two macros - one begin and one end - had been needed, but since the Devpack directives work the way they do I couldn't see a way to implement that.) The last child of a tree should (like any other last child) be preceded by 'FAMILAST name' (where 'name' in this case is the tree name). You could also write 'TREELAST name' (TREELAST simply calls FAMILAST). The only function of FAMILEND is to set the 'z'+name label, which is used to calculate the object number of the next sibling. FAMILEND can therefore be omitted if there is no next sibling (but it will do no harm to include it either). FAMILEND can always be omitted for TREES. STR and IMG ----------- To define strings and images use STR name,<'string'> IMG name image definition The string could of course be enclosed in either of " or '. The < > could be left out if the string doesn't contain any spaces. If the string is to contain a '>' this has to be written '>>'. (All this is entirely according to how Devpack works.) Note that while STR takes the actual string as a parameter and creates the necessary data directive, the IMG macro only takes the IMG name (and creates a label). The actual image data have to be created with explicit DC or INCBIN directives following the IMG macro. The name used with the string/image should be the same as used with the object that is to make use of it (and with any TEDINFO, BITBLK or ICONBLK). Or, if not used in an object, it could, within an RSC, be used as a free image or free string. The macros for the latter are: FREESTR name FREEIMG name (These simply add an address pointer to the table of free strings/images respectively.) Objects and secondary structures: --------------------------------- BITBLK name,w(bytes),h(pixels),x-offs,y-offs,colour ICONBLK name,mask,str,fgcol,bgcol,letter,let-x,y,img-x,y,w,h,txt-x,y,w,h TEDINFO name,templ,valstr,font,justif,bordcol,txtcol,txtbg,fill,bordthck APPLBLK name,drawroutine(,32bit_data) Data can be omitted Note 1: The x and y of BITBLK are offsets (in pixels) into the source image data. I don't however know what possible use could be made of them, since there isn't a corresponding way to reduce the width and height of the used image block. Instead the full image dimensions will always be used, resulting in garbage shown on screen when x and y aren't zero. Note 2: The x and y of the ICONBLK image and text refer to the position of image and text, respectively, relative to the object. I.e. image and text are totally independently positioned. Negative values can be used and image or text can be placed outside of the area defined for the object (which could well be zero). The x and y of the icon letter, on the other hand, refer to the position relative to the image. The icon text will be centred within the area defined for it (which will be cleared), and normally the width should be 6*(string length + possibly some 2 extra characters for margins) and the height about 8. (I could have let the macro calculate this automatically, but since taste may differ I refrained from that.) All measures and coordinates of the ICONBLK are in pixels. BOX name,flags,state,bordthck,bordcol,fill,x,y,w,h IBOX name,flags,state,bordthck,bordcol,x,y,w,h BOXCHAR name,flags,state,char,bordthck,bordcol,txtcol,txtbg,fill,x,y,b,h BUTTON name,flags,state,x,y,w,h STRING name,flags,state,x,y TITLE name,flags,state,x TEXT name,flags,state,x,y,w,h BOXTEXT name,flags,state,x,y,w,h FTEXT name,flags,state,x,y,w,h FBOXTEXT name,flags,state,x,y,w,h IMAGE name,flags,state,x,y,w,h ICON name,flags,state,x,y,w,h PROGDEF name,flags,state,x,y,w,h Menus: ------ To further simplify creation of menus, the following macros could be used in place of the above tree and object macros: MENUBAR name,title1,title2,etc max 9 titles DROPBOX {itemname,state,} max 17 items (incl separator lines) MENUBAR takes a menu name, which is used as a tree name, plus up to 9 title names, for each of which there should be a STR-defined string. DROPBOX takes pairs of parameters (up to 17 pairs or 34 parameters), where the first parameter is the item name (same name has to be used with a STR). The second parameter in each pair is the state, but can be omitted if no state is used. I.e. the item name can be followed by a double comma after which comes the next item. Note: As many DROPBOXes have to follow directly after a MENUBAR as there were titles defined with the MENUBAR. With just the two above macros a complete menu can be constructed. (Except that strings also have to be defined, with STR macros, for each title and item.) Example RSC: ------------ BEG_RSC STR desk,<" TESTPROG "> STR file,<" File "> STR opts,<" Options "> STR separ,<"--------------------"> STR one,<"1"> STR two,<"2"> STR three,<"3"> STR four,<"4"> STR five,<"5"> STR six,<"6"> STR load,<" Load file ^L "> STR save,<" Save file ^S "> STR doit,<" Do it! D "> STR about,<' about TESTPROG '> STR tstb,<'TEST BOX'> STR hi,<'Hi there!'> STR ok,<'OK'> IMG imag dc.l %00000000000000000000000000000000 dc.l %00000000001111111111110000000000 dc.l %00000001110000000000001110000000 dc.l %00000110000000000000000001100000 dc.l %00001000000000000000000000010000 dc.l %00010000000000000000000000001000 dc.l %00100000000110000001100000000100 dc.l %00100000001111000011110000000100 dc.l %01000000001111000011110000000010 dc.l %01000010000110000001100001000010 dc.l %01000001000000000000000010000010 dc.l %01000000100000000000000100000010 dc.l %00100000010000000000001000000100 dc.l %00100000001100000000110000000100 dc.l %00010000000011111111000000001000 dc.l %00001000000000000000000000010000 dc.l %00000110000000000000000001100000 dc.l %00000001110000000000001110000000 dc.l %00000000001111111111110000000000 dc.l %00000000000000000000000000000000 IMG mask dc.l %00000000011111111111111000000000 dc.l %00000011111111111111111111000000 dc.l %00001111111111111111111111110000 dc.l %00011111111111111111111111111000 dc.l %00111111111111111111111111111100 dc.l %01111111111111111111111111111110 dc.l %01111111111111111111111111111110 dc.l %11111111111111111111111111111111 dc.l %11111111111111111111111111111111 dc.l %11111111111111111111111111111111 dc.l %11111111111111111111111111111111 dc.l %11111111111111111111111111111111 dc.l %11111111111111111111111111111111 dc.l %01111111111111111111111111111110 dc.l %01111111111111111111111111111110 dc.l %00111111111111111111111111111100 dc.l %00011111111111111111111111111000 dc.l %00001111111111111111111111110000 dc.l %00000011111111111111111111000000 dc.l %00000000011111111111111000000000 ICONBLK imag,mask,hi,BLACK,WHITE,0,0,0,-16,-7,32,20,-33,-17,66,8 TEDINFO tstb,null,null,SYSFONT,CENTRE,BLACK,WHITE,TRANSPAR,RED+FILL7,0 *** THE MENU *** MENUBAR tstmenu,desk,file,opts DROPBOX about,,separ,DISABLED,one,,two,,three,,four,,five,,six DROPBOX load,,save DROPBOX doit *** THE DIALOGUE BOX *** TREE hidial BOX hidial,NOFLAG,OUTLINED,1,BLACK,RED+FILL1,0,0,10,10 BOXTEXT tstb,,,0,0,10,2 ICON imag,,,5,4,0,0 FAMILBEG midbox IBOX midbox,NOFLAG,,0,BLACK,0,5,10,3 BOX radio1,SELECTAB+RADIO,SHADOWED,1,BLACK,BLACK+FILL0,2,1,2,1 FAMILAST midbox BOX radio2,SELECTAB+RADIO,SHADOWED+SELECTED,1,BLACK,BLACK+FILL0,6,1,2,1 FAMILEND midbox FAMILAST hidial BUTTON ok,SELECTAB+DEFAULT+EXIT,,2,8,6,1 END_RSC * RSCDEFS * By: Mrten Lindstrm * Macro definitions to simplify the coding of GEM objects, trees and * complete RSC-files directly in the assembler source text. * object flags: NOFLAG = 0 SELECTAB = 1 DEFAULT = 2 EXIT = 4 TOUCHXIT = $40 EDITABLE = 8 RADIO = $10 *LAST = $20 HIDETREE = $80 INDIRECT = $100 FL3DIND = $200 FL3DACT = $400 FL3DBAK = $600 * object state: NOSTATE = 0 SELECTED = 1 CROSSED = 2 CHECKED = 4 DISABLED = 8 OUTLINED = $10 SHADOWED = $20 * colours etc WHITE = 0 BLACK = 1 RED = 2 GREEN = 3 BLUE = 4 CYAN = 5 YELLOW = 6 MAGENTA = 7 LWHITE = 8 LBLACK = 9 LRED = 10 LGREEN = 11 LBLUE = 12 LCYAN = 13 LYELLOW = 14 LMAGENTA = 15 OPAQUE = $80 TRANSPAR = 0 FILL0 = 0 FILL1 = $10 FILL2 = $20 FILL3 = $30 FILL4 = $40 FILL5 = $50 FILL6 = $60 FILL7 = $70 * fonts (in TEDINFOS): SYSFONT = 3 MINIFONT = 5 * justification (TEDINFOS): LEFT = 0 RIGHT = 1 CENTRE = 2 *Initiate variables * olevel = Object level (0=root, 1 = its direct children, etc.) * begnum = # of start object (on current level) * begnum1+ = ditto on superior levels rscnum set 0 menunum set 0 treenxt set 0 no_rsc MACRO tree1st set treenxt olevel set 0 begnum set 0 begnum1 set 0 begnum2 set 0 begnum3 set 0 rscpart set 0 ENDM no_rsc BEG_RSC MACRO RSC\: dc.w 0 dc.w OBJS\-RSC\ dc.w TEDS\-RSC\ dc.w IBLS\-RSC\ dc.w BBLS\-RSC\ dc.w FSTB\-RSC\ dc.w STRS\-RSC\ dc.w IMGS\-RSC\ dc.w FITB\-RSC\ dc.w TRTB\-RSC\ dc.w (TRTB\-OBJS\)/2/12 dc.w (FSTB\-TRTB\)/4 dc.w (OBJS\-TEDS\)/2/14 dc.w (TEDS\-IBLS\)/2/18 dc.w (IBLS\-BBLS\)/2/8 dc.w (FITB\-FSTB\)/4 dc.w (RSCEND\-FITB\)/4 dc.w RSCEND\-RSC\ STRS\: no_rsc rscpart set 1 ENDM *************************** tstnpar MACRO IFNE \1 FAIL Wrong number of parameters ENDC ENDM chknpar MACRO IFGT \1 FAIL Too many parameters ENDC ENDM *************************** idxtr MACRO IFGT treenxt-tree1st dc.l tree\ tree1st set tree1st+1 idxtr ENDC ENDM *-------------------------- steprsc MACRO IFGT rscpart-\1 FAIL Wrong order in RSC ENDC IFEQ rscpart-1 EVEN IMGS\: rscpart set 2 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-2 BBLS\: rscpart set 3 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-3 IBLS\: rscpart set 4 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-4 TEDS\: rscpart set 5 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-5 OBJS\: rscpart set 6 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-6 TRTB\: idxtr FSTB\: rscpart set 7 IFEQ rscpart-\1 MEXIT ENDC ENDC IFEQ rscpart-7 FITB\: rscpart set 8 IFEQ rscpart-\1 MEXIT ENDC ENDC RSCEND\: ENDM *-------------------------- do_rsc MACRO number of rsc part IFNE rscpart-\1 IFNE rscpart steprsc \1 ENDC ENDC ENDM *************************** STR MACRO name,<'string'> do_rsc 1 tstnpar \#-2 s\1 dc.b \2 IFND snull lnull = 1 snull ENDC dc.b 0 l\1 = *-s\1 ENDM IMG MACRO name do_rsc 2 even i\1 tstnpar \#-1 ENDM BITBLK MACRO name(image),w(bytes),h(pixels),image-x,image-y,colour do_rsc 3 tstnpar \#-6 IFNE \2-2*(\2/2) FAIL Image width in bytes not even ENDC b\1 dc.l i\1 dc.w \2 dc.w \3 dc.w \4 dc.w \5 dc.w \6 ENDM ICONBLK MACRO name,msk,str,fgcol,bgcol,lettr,let-x,y,img-x,y,w,h,txt-x,y,w,h do_rsc 4 tstnpar \#-16 IFNE \9-16*(\9/16) FAIL Icon width not divisible by 16 ENDC b\1 dc.l i\2 dc.l i\1 dc.l s\3 dc.b \4<<4|\5 dc.b \6 dc.w \7 dc.w \8 dc.w \9 dc.w \A dc.w \B dc.w \C dc.w \D dc.w \E dc.w \F dc.w \G ENDM TEDINFO MACRO name,templ,valstr,font,justif,bordcol,txtcol,txtbg,fill,bordthck do_rsc 5 tstnpar \#-10 even t\1 dc.l s\1 dc.l s\2 dc.l s\3 dc.w \4 dc.w 6 dc.w \5 dc.b \6<<4|\7 dc.b \8|\9 dc.w 0 dc.w \A dc.w l\1 dc.w l\2 ENDM FREESTR MACRO name do_rsc 7 \1 = (*-FSTB\)/4 dc.l s\1 ENDM FREEIMG MACRO name do_rsc 8 \1 = (*-FITB\)/4 dc.l i\1 ENDM END_RSC MACRO do_rsc 9 no_rsc ENDM *************************** TREE MACRO tree_name do_rsc 6 IFNE olevel FAIL Object level error ENDC IFEQ rscpart \1 ELSEIF tree\ = * \1 = treenxt-tree1st treenxt SET treenxt+1 ENDC rootad SET * dc.w -1 sibdone SET -1 begnum4 SET begnum3 begnum3 SET begnum2 begnum2 SET begnum1 begnum1 SET begnum begnum SET (*-rootad)/24 olevel SET olevel+1 dc.w begnum+1 dc.w (y\1-rootad)/24 chlddone SET -1 ENDM FAMILBEG MACRO object name (up to 7 letters) IFGT olevel-4 FAIL Too deep object level ENDC IFEQ sibdone dc.w (z\1-rootad)/24 ENDC sibdone SET -1 begnum4 SET begnum3 begnum3 SET begnum2 begnum2 SET begnum1 begnum1 SET begnum \1 = (*-rootad)/24 begnum SET \1 olevel SET olevel+1 dc.w begnum+1 dc.w (y\1-rootad)/24 chlddone SET -1 ENDM FAMILAST MACRO object_name y\1 dc.w begnum sibdone SET -1 begnum SET begnum1 begnum1 SET begnum2 begnum2 SET begnum3 begnum3 SET begnum4 olevel set olevel-1 ENDM TREELAST MACRO tree_name FAMILAST \1 ENDM FAMILEND MACRO object_name z\1 ENDM OBPART1 MACRO object type,name,flags,state IFEQ sibdone dc.w (*-rootad)/24+1 ENDC IFEQ chlddone dc.l -1 IFND \2 \2 = (*-rootad)/24 ENDC ENDC sibdone SET 0 chlddone SET 0 dc.w \1 IFC '','\3' fl set 0 ELSEIF fl set \3 ENDC IFGT olevel dc.w fl ELSEIF dc.w fl|$20 ENDC IFC '','\4' dc.w 0 ELSEIF dc.w \4 ENDC ENDM INDOBJ MACRO object type,overparam,name,flags,state,specadr,x,y,w,h tstnpar \2 OBPART1 \1,\3,\4,\5 dc.l \6 dc.w \7 dc.w \8 dc.w \9 dc.w \A ENDM BOXOBJ MACRO obj type,overpar,name,lg,state,char,bordthck,b&tcol,fill,x,y,b,h tstnpar \2 OBPART1 \1,\3,\4,\5 dc.b \6 dc.b \7 dc.b \8 dc.b \9 dc.w \A dc.w \B dc.w \C dc.w \D ENDM BOX MACRO name,flags,state,bordthck,bordcol,fill,x,y,w,h BOXOBJ 20,\#-10,\1,\2,\3,0,\4,\5<<4,\6,\7,\8,\9,\A ENDM IBOX MACRO name,flags,state,bordthck,bordcol,x,y,w,h BOXOBJ 25,\#-9,\1,\2,\3,0,\4,\5<<4,0,\6,\7,\8,\9 ENDM BOXCHAR MACRO name,flags,state,char,bordthck,bordcol,txtcol,txtbg,fill,x,y,w,h BOXOBJ 27,\#-13,\1,\2,\3,\4,\5,\6<<4|\7,\8|\9,\A,\B,\C,\D ENDM BUTTON MACRO name,flags,state,x,y,w,h INDOBJ 26,\#-7,\1,\2,\3,s\1,\4,\5,\6,\7 ENDM STRING MACRO name,flags,state,x,y INDOBJ 28,\#-5,\1,\2,\3,s\1,\4,\5,l\1-1,1 ENDM TITLE MACRO name,flags,state,x INDOBJ 32,\#-4,\1,\2,\3,s\1,\4,0,l\1-1,$301 ENDM TEXT MACRO name,flags,state,x,y,w,h INDOBJ 21,\#-7,\1,\2,\3,t\1,\4,\5,\6,\7 ENDM BOXTEXT MACRO name,flags,state,x,y,w,h INDOBJ 22,\#-7,\1,\2,\3,t\1,\4,\5,\6,\7 ENDM FTEXT MACRO name,flags,state,x,y,w,h INDOBJ 29,\#-7,\1,\2,\3,t\1,\4,\5,\6,\7 ENDM FBOXTEXT MACRO name,flags,state,x,y,w,h INDOBJ 30,\#-7,\1,\2,\3,t\1,\4,\5,\6,\7 ENDM IMAGE MACRO name,flags,state,x,y,w,h INDOBJ 23,\#-7,\1,\2,\3,b\1,\4,\5,\6,\7 ENDM ICON MACRO name,flags,state,x,y,w,h INDOBJ 31,\#-7,\1,\2,\3,b\1,\4,\5,\6,\7 ENDM PROGDEF MACRO name,flags,state,x,y,w,h INDOBJ 24,\#-7,\1,\2,\3,a\1,\4,\5,\6,\7 ENDM APPLBLK MACRO name,drawroutine(,32bit_data) Data can be omitted a\1 dc.l \2 IFEQ \#-3 dc.l \3 ENDC ENDM *************************** MENUBAR MACRO name,title1,title2,etc max 9 titles chknpar \#-10 TREE \1 IBOX \1,NOFLAG,NOSTATE,0,WHITE,0,0,80,25 titlnum SET 1 ntitles SET \#-1 mnwidth SET l\2-1 IFGT \#-2 mnwidth SET mnwidth+l\3-1 IFGT \#-3 mnwidth SET mnwidth+l\4-1 IFGT \#-4 mnwidth SET mnwidth+l\5-1 IFGT \#-5 mnwidth SET mnwidth+l\6-1 IFGT \#-6 mnwidth SET mnwidth+l\7-1 IFGT \#-7 mnwidth SET mnwidth+l\8-1 IFGT \#-8 mnwidth SET mnwidth+l\9-1 IFGT \#-9 mnwidth SET mnwidth+l\A-1 ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC FAMILBEG BAR\@ BOX BAR\@,NOFLAG,NOSTATE,0,BLACK,WHITE,0,0,80,$201 FAMILAST BAR\@ FAMILBEG ACTV\@ IBOX ACTV\@,NOFLAG,NOSTATE,0,WHITE,2,0,mnwidth,$301 titl1_x SET 0 titl2_x SET 0 titl3_x SET 0 titl4_x SET 0 titl5_x SET 0 titl6_x SET 0 titl7_x SET 0 titl8_x SET 0 titl9_x SET 0 IFLE \#-2 FAMILAST ACTV\@ TITLE \2,NOFLAG,NOSTATE,titl1_x ELSEIF TITLE \2,NOFLAG,NOSTATE,titl1_x titl2_x SET titl1_x+l\2-1 IFLE \#-3 FAMILAST ACTV\@ TITLE \3,NOFLAG,NOSTATE,titl2_x ELSEIF TITLE \3,NOFLAG,NOSTATE,titl2_x titl3_x SET titl2_x+l\3-1 IFLE \#-4 FAMILAST ACTV\@ TITLE \4,NOFLAG,NOSTATE,titl3_x ELSEIF TITLE \4,NOFLAG,NOSTATE,titl3_x titl4_x SET titl3_x+l\4-1 IFLE \#-5 FAMILAST ACTV\@ TITLE \5,NOFLAG,NOSTATE,titl4_x ELSEIF TITLE \5,NOFLAG,NOSTATE,titl4_x titl5_x SET titl4_x+l\5-1 IFLE \#-6 FAMILAST ACTV\@ TITLE \6,NOFLAG,NOSTATE,titl5_x ELSEIF TITLE \6,NOFLAG,NOSTATE,titl5_x titl6_x SET titl5_x+l\6-1 IFLE \#-7 FAMILAST ACTV\@ TITLE \7,NOFLAG,NOSTATE,titl6_x ELSEIF TITLE \7,NOFLAG,NOSTATE,titl6_x titl7_x SET titl6_x+l\7-1 IFLE \#-8 FAMILAST ACTV\@ TITLE \8,NOFLAG,NOSTATE,titl7_x ELSEIF TITLE \8,NOFLAG,NOSTATE,titl7_x titl8_x SET titl7_x+l\8-1 IFLE \#-9 FAMILAST ACTV\@ TITLE \9,NOFLAG,NOSTATE,titl8_x ELSEIF TITLE \9,NOFLAG,NOSTATE,titl8_x titl9_x SET titl8_x+l\9-1 FAMILAST ACTV\@ TITLE \A,NOFLAG,NOSTATE,titl9_x ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC FAMILEND ACTV\@ FAMILEND BAR\@ FAMILAST \1 FAMILBEG SCRN_m\ IBOX ,NOFLAG,NOSTATE,0,WHITE,0,$301,80,19 (height=19) ENDM *************************** setmaxmw MACRO IFGT l\1-1-mnwidth mnwidth SET l\1-1 ENDC ENDM mstring MACRO name,state,y INDOBJ 28,\#-3,\1,NOFLAG,\2,s\1,0,\3,mnwidth,1 ENDM *-------------------------- DROPBOX MACRO {itemname,state} ntitles SET ntitles-1 IFEQ ntitles FAMILAST SCRN_m\ menunum set menunum+1 ENDC FAMILBEG DROP\@ mnwidth SET l\1-1 IFGT \#-2 setmaxmw \3 IFGT \#-4 setmaxmw \5 IFGT \#-6 setmaxmw \7 IFGT \#-8 setmaxmw \9 IFGT \#-10 setmaxmw \B IFGT \#-12 setmaxmw \D IFGT \#-14 setmaxmw \F IFGT \#-16 setmaxmw \H IFGT \#-18 setmaxmw \J IFGT \#-20 setmaxmw \L IFGT \#-22 setmaxmw \N IFGT \#-24 setmaxmw \P IFGT \#-26 setmaxmw \R IFGT \#-28 setmaxmw \T IFGT \#-30 setmaxmw \V IFGT \#-32 setmaxmw \X ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC BOX ,NOFLAG,NOSTATE,-1,BLACK,WHITE,titl\_x+2,0,mnwidth,(\#+1)/2 titlnum SET titlnum+1 IFLE \#-2 FAMILAST DROP\@ mstring \1,\2,0 ELSEIF mstring \1,\2,0 IFLE \#-4 FAMILAST DROP\@ mstring \3,\4,1 ELSEIF mstring \3,\4,1 IFLE \#-6 FAMILAST DROP\@ mstring \5,\6,2 ELSEIF mstring \5,\6,2 IFLE \#-8 FAMILAST DROP\@ mstring \7,\8,3 ELSEIF mstring \7,\8,3 IFLE \#-10 FAMILAST DROP\@ mstring \9,\A,4 ELSEIF mstring \9,\A,4 IFLE \#-12 FAMILAST DROP\@ mstring \B,\C,5 ELSEIF mstring \B,\C,5 IFLE \#-14 FAMILAST DROP\@ mstring \D,\E,6 ELSEIF mstring \D,\E,6 IFLE \#-16 FAMILAST DROP\@ mstring \F,\G,7 ELSEIF mstring \F,\G,7 IFLE \#-18 FAMILAST DROP\@ mstring \H,\I,8 ELSEIF mstring \H,\I,8 IFLE \#-20 FAMILAST DROP\@ mstring \J,\K,9 ELSEIF mstring \J,\K,9 IFLE \#-22 FAMILAST DROP\@ mstring \L,\M,10 ELSEIF mstring \L,\M,10 IFLE \#-24 FAMILAST DROP\@ mstring \N,\O,11 ELSEIF mstring \N,\O,11 IFLE \#-26 FAMILAST DROP\@ mstring \P,\Q,12 ELSEIF mstring \P,\Q,12 IFLE \#-28 FAMILAST DROP\@ mstring \R,\S,13 ELSEIF mstring \R,\S,13 IFLE \#-30 FAMILAST DROP\@ mstring \T,\U,14 ELSEIF mstring \T,\U,14 IFLE \#-32 FAMILAST DROP\@ mstring \V,\W,15 ELSEIF mstring \V,\W,15 IFLE \#-34 FAMILAST DROP\@ mstring \X,\Y,16 ELSEIF mstring \X,\Y,16 ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC ENDC FAMILEND DROP\@ ENDM * 'FAMILEND SCRN' and 'FAMILEND MENU' were possible to omit -------------- RSC INLINE -------------- by: Mrten Lindstrm Routines to enable use of GEM resources incorporated in program file. SUB-ROUTINE NAME RSCREL BRIEF DESCRIPTION Relocates RSC file FILENAME RSCINLIN.S OTHER RESOURCES - LANGUAGE Assembler (Devpac 3) AUTHOR Mrten Lindstrm ENTRY PARAMETER A4->file to be relocated EXIT PARAMETER D0: 1=No error, 0=Unknown elements in file DETAILS RSCREL is needed (only) with RSC files included as they stands as binary files (probably with INCBIN in Devpack). It relocates all longword pointers in RSC file pointed to by A4. I.e. adds address of file start to them. D0 will be zero on return (and processor Z flag set) if any unknown element (unknown object type) was encountered in file; even in this case all recognized pointers will have been relocated though. As it stands RSCREL will only relocate pre-TOS4 RSC files, i.e. excluding colour icons. But I have included a code sequence to deal with TOS4+ extended RSC files as well, as comment lines. All you have to do is to uncomment this sequence. (Be warned though that I haven't been able to test this part, since I haven't got TOS4 - or a Falcon - of my own.) Example: lea rscfile,A4 bsr rscrel ble error ... rscfile: INCBIN X.RSC SUB-ROUTINE NAME RSCFIX, TREEFIX BRIEF DESCRIPTION Convert character coordinates FILENAME RSCINLIN.S OTHER RESOURCES Devpack AES library (for RSRC_OBFIX) LANGUAGE Assembler (Devpac 3) AUTHOR Mrten Lindstrm ENTRY PARAMETER For RSCFIX : A4 -> file to be converted For TREEFIX: A3 -> tree to be converted EXIT PARAMETERS None DETAILS One of RSCFIX and TREEFIX is needed with any GEM resource or tree that isn't loaded with RSRC_LOAD. I.e. any 'included' resource/tree, whether as source or as binary file. They convert coordinates from characters to pixels in resource file or single tree respectively. Internally RSCFIX calls TREEFIX for each tree in the resource. TREEFIX in its turn calls the AES function RSRC_OBFIX - in the form used in the Devpack macros, as TREEFIX is now written. Note: I actually wrote two versions of TREEFIX - both included in the source file, one of which as comment lines. The enabled version simply goes through the object list for the tree until a set Last-object flag is encountered. The disabled version instead works its way through the tree structure, branch by branch, by calling my TREEDO routine (see below). The advantage of the second version would be that it works even should a Last-object flag be missing (though this should never be, I think) as long as the tree structure is OK. In addition it is a few bytes shorter, but on the other hand requires TREEDO to be included. SUB-ROUTINE NAME TREEDO BRIEF DESCRIPTION For each object in (sub-)tree runs custom routine FILENAME RSCINLIN.S OTHER RESOURCES - LANGUAGE Assembler (Devpac 3) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A3 -> Tree D2: Start object (= 'root of sub-tree') A0 -> Custom sub-routine to run for each object EXIT PARAMETERS D0 will contain whatever custom routine sets it to DETAILS Will for each object in a (sub-)tree run a custom sub-routine pointed to by A0. The main tree should be pointed to by A3, and the number of the start object should be given in D2. (If D2=0 the whole tree will be done.) At sub-routine entry A3 will point to the tree, A2 will point to the object and D2 will contain the object number. All registers can be freely used and all registers EXCEPT D0 will be restored at TREEDO exit. D0 will contain whatever the custom sub-routine sets it to; TREEDO won't touch it. Processor condition flags will at exit be set according to a test of D0.L. (Should you want word format for the test of the exit parameter you will have to change TREEDO correspondingly.) TREEDO isn't particularly needed for 'inline resources' and could be of as much use with ordinary RSC files loaded with RSRC_LOAD, so should perhaps not have been placed within this library. Of the many things it could be used for, one would be the fixing of hidden sub-trees with possible editable objects in them (see the resource info file, that I send to Ictari together with this file). SUB-ROUTINE NAME GETTREE BRIEF DESCRIPTION Get tree address FILENAME RSCINLIN.S OTHER RESOURCES - LANGUAGE Assembler (Devpac 3) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> RSC file D3: Tree number EXIT PARAMETERS A3 -> Tree D0: 1=OK, 0= This high tree number doesn't exist DETAILS Loads A3 with address of tree, within resource pointed to by A4 and the number of which should be given in D3. At exit D0 (and processor flags) set according to: 1=OK, 0= Tree of this high number doesn't exist in resource. * -------------- * RSC INLINE * -------------- * By: Mrten Lindstrm * * Routines to enable use of GEM resources incorporated in program file *************************** * RSCREL Relocate RSC file *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A4 -> RSC file to be relocated * OUT: D0: 1=No error, 0=Unknown elements in file RSCREL movem.l D1-D3/A0-A1/A4,-(SP) move.l A4,D1 moveq #1,D3 Return code: Initiate to no error moveq #0,D0 lea (A4),A0 move.w 4(A4),D0 add.l D0,A0 A0->TEDINFOs move.w 24(A4),D0 D0: Number of TEDINFOs bra.s rscrel2 rscrel1 add.l D1,(A0)+ add.l D1,(A0)+ add.l D1,(A0)+ lea 16(A0),A0 rscrel2 dbf D0,rscrel1 lea (A4),A0 move.w 6(A4),D0 add.l D0,A0 A0->ICONBLKs move.w 26(A4),D0 D0: Number of ICONBLKs bra.s rscrel4 rscrel3 add.l D1,(A0)+ add.l D1,(A0)+ add.l D1,(A0)+ lea 22(A0),A0 tst.l (A0) bne.s rscrel4 addq.l #2,A0 rscrel4 dbf D0,rscrel3 lea (A4),A0 move.w 8(A4),D0 add.l D0,A0 A0->BITBLKs move.w 28(A4),D0 D0: Number of BITBLKs bra.s rscrel6 rscrel5 add.l D1,(A0)+ lea 10(A0),A0 tst.l (A0) bne.s rscrel6 addq.l #2,A0 rscrel6 dbf D0,rscrel5 lea (A4),A0 move.w 10(A4),D0 add.l D0,A0 A0->free strings move.w 30(A4),D0 D0: Number of free strings bra.s rscrel8 rscrel7 add.l D1,(A0)+ rscrel8 dbf D0,rscrel7 lea (A4),A0 move.w 16(A4),D0 add.l D0,A0 A0->free images move.w 32(A4),D0 D0: Number of free images bra.s rscrel10 rscrel9 add.l D1,(A0)+ rscrel10 dbf D0,rscrel9 lea (A4),A0 move.w 18(A4),D0 add.l D0,A0 A0->trees move.w 22(A4),D0 D0: Number of trees bra.s rscrel12 rscrel11 add.l D1,(A0)+ rscrel12 dbf D0,rscrel11 lea (A4),A0 move.w 2(A4),D0 add.l D0,A0 A0->Objects move.w 20(A4),D0 D0: Number of objects bra.s rscrel16 rscrel13 moveq #0,D3 Unknown object type bra.s rscrel15 rscrel14 moveq #-20,D2 Relocate next object add.b 7(A0),D2 Object basic type -20 beq.s rscrel15 G_BOX bmi.s rscrel13 subq.b #5,D2 beq.s rscrel15 G_IBOX subq.b #2,D2 beq.s rscrel15 G_BOXCHAR subq.b #6,D2 bgt.s rscrel13 Greater than G_CICON => unknown add.l D1,12(A0) rscrel15 lea 24(A0),A0 rscrel16 dbf D0,rscrel14 * Uncomment the following if you use colour icons for TOS 4+ * - - - - - - - - - - - - - Relocate any TOS 4+ extension * moveq #4,D0 * and.w (A4),D0 * beq.s rscrel25 * move.w 34(A4),D0 * add.l D0,A4 * addq.l #4,A4 * lea (A4),A0 * move.l (A0),D2 * bgt.s rscrel18 *rscrel17 move.l (A0),D2 Relocate extension header pointer table * ble.s rscrel19 * moveq #0,D3 *rscrel18 add.l D1,D2 *rscrel19 move.l D2,(A0)+ * bne.s rscrel17 * move.l (A4),A0 A0-> pointer table * move.l A0,D2 * ble.s rscrel25 ** - - - - - - - - - - - - - Set pointers in CICONBLKs and related structures * lea (A0),A4 * * moveq #-1,D2 *rscrel20 addq.w #1,D2 Forward A0 to end of pointer table * tst.l (A0)+ and count number of CICONBLKs * bpl.s rscrel20 * bra.s rscrel24 D2: Number of CICONBLKs * *rscrel21 move.l A0,(A4)+ Do CICONBLK. A0: Pointer to CICONBLK * move.w 22(A0),D1 * lsr.w #3,D1 * mulu 24(A0),D1 D1: bytes per plane * lea 38(A0),A1 * move.l A1,(A0)+ Pointer to mono data * add.l D1,A1 * move.l A1,(A0)+ Pointer to mono mask * add.l D1,A1 * move.l A1,(A0)+ Pointer to icon text * lea 12(A1),A1 * lea 22(A0),A0 * *rscrel22 exg A0,A1 Do CICON * tst.l (A1) * beq.s rscrel24 No CICON * move.l A0,(A1) A0: Pointer to CICON * move.w (A0)+,D0 * lea 20(A0),A1 * move.l A1,(A0)+ Pointer to unselected image * mulu D1,D0 D0: bytes in image, assuming * add.l D0,A1 less than 32K/plane * move.l A1,(A0)+ Pointer to unselected mask * add.l D1,A1 * tst.l (A0) * bne.s rscrel23 * addq.l #8,A0 * bra.s rscrel22 *rscrel23 move.l A1,(A0)+ Pointer to selected image * add.l D0,A1 * move.l A1,(A0)+ Pointer to selected mask * add.l D1,A1 * bra.s rscrel22 * *rscrel24 dbf D2,rscrel21 * - - - - - - - - - - - - - rscrel25 movem.l (SP)+,D1-D3/A0-A1/A4 move.l D3,D0 rts *************************** * RSCFIX, TREEFIX Convert character coordinates *~~~~~~~~~~~~~~~~~~~~~~~~~~ into pixels * IN: A4 -> RSC file * or A3 -> TREE, respectively RSCFIX movem.l D1/A1/A3,-(SP) lea (A4),A1 moveq #0,D1 move.w 18(A4),D1 add.l D1,A1 A1-> Table of pointers to trees move.w 22(A4),D1 D1: Number of trees bra.s rscfix2 rscfix1 move.l (A1)+,A3 bsr.s TREEFIX rscfix2 dbf D1,rscfix1 movem.l (SP)+,D1/A1/A3 rts *========================== TREEFIX movem.l D0-D3/A0-A2,-(SP) moveq #0,D3 treefix1 rsrc_obfix A3,D3 moveq #24,D0 mulu D3,D0 addq.w #1,D3 btst #5,9(A3,D0.W) Last object? beq.s treefix1 movem.l (SP)+,D0-D3/A0-A2 rts * Alternative version making use of TREEDO below: *TREEFIX movem.l D0/D2/A0,-(SP) * lea treefix1(PC),A0 To do for each object * moveq #0,D2 Begin with root * bsr.s TREEDO * movem.l (SP)+,D0/D2/A0 * rts *treefix1 rsrc_obfix A3,D2 * rts *************************** * TREEDO *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A3 -> Tree, D2: Object to start with. Do it plus its offspring * A0 -> Sub-routine to run for each object * At routine entry A3 will point to the tree, A2 will point to the object, * D2 will contain the object number. * All registers can be freely used. * All registers EXCEPT D0 will be restored at TREEDO exit. * D0 will contain whatever the routine sets it to, TREEDO won't touch it TREEDO movem.l D1-D7/A0-A2/A4-A6,-(SP) moveq #24,D1 mulu D2,D1 move.w 0(A3,D1.L),D1 bsr.s treedo1 movem.l (SP)+,D1-D7/A0-A2/A4-A6 tst.l D0 rts treedo1 lea (A3),A2 moveq #24,D3 mulu D2,D3 add.l D3,A2 A2-> object movem.l D1-D2/A0/A2-A3,-(SP) jsr (A0) movem.l (SP)+,D1-D2/A0/A2-A3 move.w (A2)+,-(SP) Save next sibling move.w (A2),D3 D3: First child bmi.s treedo2 move.w D1,-(SP) move.w D2,D1 Let object be parent for a while move.w D3,D2 Start with first child bsr.s treedo1 Do the children (recursively) move.w (SP)+,D1 Back to same old parent ... treedo2 move.w (SP)+,D2 ... but do next sibling cmp.w D1,D2 Make sure it isn't parent bne.s treedo1 rts *************************** * GETTREE Get tree address *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A4 -> RSC file, D3.W: Tree number * OUT: A3 -> Tree, D0: 1=OK, 0 = Tree with this high number doesn't exist GETTREE cmp.w 22(A4),D3 bcc.s gettree1 error lea (A4),A3 moveq #0,D0 move.w 18(A4),D0 add.l D0,A3 A3-> Table of pointers to trees move.w D3,D0 lsl.w #2,D0 add.w D0,A3 move.l (A3),A3 A3-> Tree moveq #1,D0 rts gettree1 moveq #0,D0 rts . e<.. eDEMO1 S ==DEMO RSC =?*DEMO I =@DEMO HRD =AREADME TXT =B*DEMO RS =CDEMO1 PRG =GDEMO2 PRG =I0 DEMO2 S =L* * Using External and Embedded Resource files in Assembler * Demo for Ictari by Keith Baines, January 1995 * * Version 1 - External Resource * INCLUDE GEMMACRO.I HiSoft GEM macros INCLUDE DEMO.I RSC header file from Wercs ; Set up stack and shrink memory MOVEA.L A7,A6 Save original stack LEA MyStack,A7 Set stack in kept area MOVEA.L 4(A6),A6 Ax=address of base page MOVE.L $C(A6),D0 D0=length of text ADD.L $14(A6),D0 + length of data ADD.L $1C(A6),D0 + length of BSS ADDI.L #$100,D0 + length of base-page MOVE.L D0,-(A7) Size to keep MOVE.L A6,-(A7) Address to keep CLR.W -(A7) Zero word move.w #$4A,-(A7) mem shrink call trap #1 lea 12(A7),A7 appl_init bsr load_rsc tst.w D0 beq.s bad_rsc move.l #FirstDialog,D0 bsr do_dialog move.l #SecondDialog,D0 bsr do_dialog appl_exit clr.w -(A7) trap #1 bad_rsc form_alert #1,#cant_load clr.w -(A7) trap #1 * Load the resource file load_rsc rsrc_load #rsc_name rts * Display the dialog do_dialog rsrc_gaddr #0,D0 move.l addr_out,A5 form_center A5 move.w int_out+2,D4 move.w int_out+4,D5 move.w int_out+6,D6 move.w int_out+8,D7 form_dial #0,D4,D5,D6,D7,D4,D5,D6,D7 objc_draw A5,#0,#9,D4,D5,D6,D7 graf_mouse #0 form_do A5,#0 form_dial #3,D4,D5,D6,D7,D4,D5,D6,D7 rts INCLUDE AESLIB.S HiSoft AES calling code SECTION DATA rsc_name DC.B "DEMO.RSC",0 cant_load DC.B "[3][ Can't load DEMO.RSC ][QUIT]",0 SECTION BSS DS.L 100 MyStack  $" *Resouces Demofor Ictariby Keith BainesJanuary 1995OKOKSome Small TextAnd an Image:ٌPPRRL@@NQDPDPDPDPDPDքDRDRDRNL@_@`pq $ 2 =M 'Z  ] r    FirstDialog EQU 0 Heading1 EQU 1 Heading2 EQU 2 Author EQU 3 VersionDate EQU 4 ExitButton EQU 5 SecondDialog EQU 1  Le FirstDialogLHeading1LHeading2LAuthorLVersionDateLExitButtonLSecondDialog _p Files in this folder: DEMO1.S } Demo program using external resource file DEMO1.PRG } DEMO2.S } Demo program with embedded resources DEMO2.PRG DEMO.RSC } DEMO.HRD } Wercs files for the resource DEMO.I } DEMO.RS Assembler version of the resource file ; RSC_TO_S -- Version 1.00 by Keith Baines 3rd January 1993 ; ; 29-01-1995 G_BOX EQU 20 G_TEXT EQU 21 G_BOXTEXT EQU 22 G_IMAGE EQU 23 G_PROGDEF EQU 24 G_IBOX EQU 25 G_BUTTON EQU 26 G_BOXCHAR EQU 27 G_STRING EQU 28 G_FTEXT EQU 29 G_FBOXTEXT EQU 30 G_ICON EQU 31 G_TITLE EQU 32 NONE EQU 0 SELECTABLE EQU 1 DEFAULT EQU 2 EXIT EQU 4 EDITABLE EQU 8 RBUTTON EQU 16 LASTOB EQU 32 TOUCHEXIT EQU 64 HIDDEN EQU 128 INDIRECT EQU 256 NORMAL EQU 0 SELECTED EQU 1 CROSSED EQU 2 CHECKED EQU 4 DISABLED EQU 8 OUTLINED EQU 16 SHADOWED EQU 32 ************************************************* ** Resource data from file A:\RSCDEMO\DEMO.RSC ** ************************************************* ;-----------------; ; RSC File Header ; ;-----------------; DC.W 1 RSC File Version DC.W $11A Start of Objects DC.W $F0 Start of TEDINFOs DC.W $10C Start of ICONBLKs DC.W $10C Start of BITBLKs DC.W $F0 List of free strings DC.W $24 Start of string data DC.W $80 Start of bit image data DC.W $11A List of free images DC.W $222 List of Trees DC.W 11 Number of Objects DC.W 2 Number of Trees DC.W 1 Number of TEDINFOs DC.W 0 Number of ICONBLKs DC.W 1 Number of BITBLKs DC.W 0 Number of Free STrings DC.W 0 Number of Free Images DC.W $22A RSC File Length ;-------------; ; String Data ; ;-------------; STR_24 DC.B 'Resouces Demo',0 STR_32 DC.B 'for Ictari',0 STR_3D DC.B 'by Keith Baines',0 STR_4D DC.B 'January 1995',0 STR_5A DC.B 'OK',0 STR_5D DC.B 'OK',0 STR_60 DC.B 'Some Small Text',0 STR_70 DC.B 0 STR_71 DC.B 0 STR_72 DC.B 'And an Image:',0 ;----------------; ; Bit Image Data ; ;----------------; ; 32 Wide by 28 High IMG_80 DC.W $0,$0 DC.W $ED9,$8CE0 DC.W $4AA,$5080 DC.W $4AA,$5080 DC.W $48B,$D6C0 DC.W $48A,$5280 DC.W $48A,$5280 DC.W $E8A,$4CE0 DC.W $0,$0 DC.W $7FFF,$FFFC DC.W $4000,$4 DC.W $4000,$4 DC.W $4E51,$8CE4 DC.W $44AA,$5084 DC.W $44AA,$5084 DC.W $44AA,$5084 DC.W $44AA,$5084 DC.W $448A,$50C4 DC.W $448B,$D684 DC.W $448A,$5284 DC.W $448A,$5284 DC.W $448A,$5284 DC.W $4E8A,$4CE4 DC.W $4000,$4 DC.W $5FFF,$FFF4 DC.W $4000,$4 DC.W $7FFF,$FFFC DC.W $0,$0 ;----------; ; TEDINFOs ; ;----------; TED_F0 DC.L STR_60,STR_70,STR_71 DC.W $5,$0,$2,$1181,$0,$0,$10,$1 ;---------; ; BITBLKs ; ;---------; BITBLK_10C DC.L IMG_80 DC.W $4,$1C,$0,$0,$1 ;-------; ; Trees ; ;-------; tr_FirstDialog DC.W -1,1,5,G_BOX DC.W NONE DC.W OUTLINED DC.W $2,$1181 DC.W $11,$303,$1B,$C ; Object 1 DC.W 2,-1,-1,G_STRING DC.W NONE DC.W NORMAL DC.L STR_24 DC.W $7,$1,$D,$1 ; Object 2 DC.W 3,-1,-1,G_STRING DC.W NONE DC.W NORMAL DC.L STR_32 DC.W $8,$3,$A,$1 ; Object 3 DC.W 4,-1,-1,G_STRING DC.W NONE DC.W NORMAL DC.L STR_3D DC.W $6,$5,$F,$1 ; Object 4 DC.W 5,-1,-1,G_STRING DC.W NONE DC.W NORMAL DC.L STR_4D DC.W $8,$7,$C,$1 ; Object 5 DC.W 0,-1,-1,G_BUTTON DC.W SELECTABLE+DEFAULT+EXIT+LASTOB DC.W NORMAL DC.L STR_5A DC.W $9,$A,$A,$1 tr_SecondDialog DC.W -1,1,4,G_BOX DC.W NONE DC.W OUTLINED DC.W $2,$1181 DC.W $712,$307,$19,$609 ; Object 1 DC.W 2,-1,-1,G_BUTTON DC.W SELECTABLE+DEFAULT+EXIT DC.W NORMAL DC.L STR_5D DC.W $7,$7,$B,$1 ; Object 2 DC.W 3,-1,-1,G_TEXT DC.W NONE DC.W NORMAL DC.L TED_F0 DC.W $1,$1,$17,$1 ; Object 3 DC.W 4,-1,-1,G_STRING DC.W NONE DC.W NORMAL DC.L STR_72 DC.W $3,$4,$D,$1 ; Object 4 DC.W 0,-1,-1,G_IMAGE DC.W LASTOB DC.W NORMAL DC.L BITBLK_10C DC.W $13,$4,$4,$2 ;---------------; ; List Of Trees ; ;---------------; LIST_222 DC.L tr_FirstDialog DC.L tr_SecondDialog ; ==== End of File ==== ;` ,OO,n . ЮЮ//Bg?<JNAO p aa@J@g <aD <a:panBgNA3#p4aRBgNA#pna>Nu33#p#3"<0<NB09*y #p6a89:9<9>9333333333p3a#33 3333p*ad3pNaV#3p2aB333333333p3aNuC2@ Ap222BQ"<0<NB09Nu  DEMO.RSC[3][ Can't load DEMO.RSC ][QUIT]globalint_inMyStackaddr_inbad_rscZcontrolint_outCALL_AESaddr_out load_rscvrsc_namecant_loaHddo_dialoHgaes_paraHmsgem_ctrlH_listZ      HEADDBGV01 HCLNA:\RSCDEMO\DEMO1.S*< >4>HCLNAESLIB.S`0,OO,n . ЮЮ//Bg?<JNAO p ava <a\ <aRpaXBgNAGh6+I08+`,*\:<#3pra&05 fRE`QNuAh2(A*p#p6a89:9<9>9333333333p3a#33 3333p*ad3pNaV#3p2aB333333333p3aNuC2@ Ap222BQ"<0<NB09Nu   $" *Resouces Demofor Ictariby Keith BainesJanuary 1995OKOKSome Small TextAnd an Image:ٌPPRRL@@NQDPDPDPDPDPDքDRDRDRNL@_@    '   X  tIMG_80STR_24STR_32STR_3DSTR_4DSTR_5ASTR_5DSTR_60STR_70STR_71STR_72TED_F0Xglobalint_inMyStackaddr_incontrolfix_rscVint_outCALL_AESLIST_222addr_outfix_looplnext_objrdo_dialoHgBITBLK_1Ht0Caes_paraHmsend_fix_HlpTheResouHhrcegem_ctrlH_listtr_FirstHDialogtr_SeconHdDialogT$    d20 HEADDBGV01&HCLNA:\RSCDEMO\DEMO2.S6 >4>HCLNAESLIB.S* * Using External and Embedded Resource files in Assembler * Demo for Ictari by Keith Baines, January 1995 * * Version 2 - Resource Included as Assembler File * INCLUDE GEMMACRO.I HiSoft GEM macros INCLUDE DEMO.I RSC header file from Wercs ; Set up stack and shrink memory MOVEA.L A7,A6 Save original stack LEA MyStack,A7 Set stack in kept area MOVEA.L 4(A6),A6 Ax=address of base page MOVE.L $C(A6),D0 D0=length of text ADD.L $14(A6),D0 + length of data ADD.L $1C(A6),D0 + length of BSS ADDI.L #$100,D0 + length of base-page MOVE.L D0,-(A7) Size to keep MOVE.L A6,-(A7) Address to keep CLR.W -(A7) Zero word move.w #$4A,-(A7) mem shrink call trap #1 lea 12(A7),A7 appl_init bsr fix_rsc move.l #FirstDialog,D0 bsr do_dialog move.l #SecondDialog,D0 bsr do_dialog appl_exit clr.w -(A7) trap #1 * Fix-up resource co-ordinates fix_rsc lea TheResource,A3 ' start of rsc data move.w 18(A3),D3 ' offset to list of trees lea (A3,D3.W),A4 ' address of list move.w 22(A3),D4 ' number of trees bra.w end_fix_lp fix_loop move.l (A4)+,A5 ' address of first tree move.w #0,D5 ' object number in tree next_obj rsrc_obfix A5,D5 move.w D5,D0 mulu #24,D0 btst #5,9(A5,D0.L) ' last object in tree? bne.s end_fix_lp addq.w #1,D5 bra.s next_obj end_fix_lp dbra D4,fix_loop ' any more trees to do? rts * Display the dialog do_dialog lea TheResource,A0 ' start of resource data move.w 18(A0),D1 ' offset to list of trees lea (A0,D1.W),A0 ' address of list asl.l #2,D0 ' entry size = 4 move.l (A0,D0.L),A5 ' address of our tree form_center A5 move.w int_out+2,D4 move.w int_out+4,D5 move.w int_out+6,D6 move.w int_out+8,D7 form_dial #0,D4,D5,D6,D7,D4,D5,D6,D7 objc_draw A5,#0,#9,D4,D5,D6,D7 graf_mouse #0 form_do A5,#0 form_dial #3,D4,D5,D6,D7,D4,D5,D6,D7 rts INCLUDE AESLIB.S HiSoft AES calling code SECTION DATA TheResource INCLUDE "DEMO.RS" SECTION BSS DS.L 100 MyStack ICTARI USER GROUP ISSUE 21 April 1995 ___ ______ ___ _________ _________ ___ \__\ \ __\ \ \__ \______ \ \ _____\ \__\ ___ \ \ \ __\ _____\ \ \ \ ___ \ \ \ \ \ \ \ ____ \ \ \ \ \ \ \ \ \_____ \ \____ \ \__\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \__\ \_______\ \______\ \________\ \__\ \__\ * m a g a z i n e * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= I C T A R I U S E R G R O U P 63 Woolsbridge Road, Ringwood, Hants, BH24 2LX Tel. 01425-474415 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= INDEX FOR ISSUE 21 ================== ASSEMBLY Various maths function routines. RSC Macros and RSC routines. Joystick routines. RSC file inclusion in program plus demo program. C Questions and Answers file. GFA Football game code. STOS Text display program code. Fast sprite drawing routines. MISC DXF format text file. Resource file format information. RSC to source file converter program. In next months issue of ICTARI (this may change) :- ASSEMBLY Polyline drawing routines in machine code. Bezier curve drawing routines. Shape outliner routine. Intro demo program code. Mouse position test routine. Upper/lower case conversion routines. C Progress report function. Tim Oren GEM tutorial. Part 11. GEM hooks & hacks. BASIC Demo program on disabling menu items. GFA Picture clipping code. Bingo card number generator. GFA Tool Kit book review. STOS Bank display program code. Chaos engine. MISC Using the system clipboard. GDOS information file. For future issues :- Picture compression routine for IMG pictures. HP DeskJet/LaserJet compression routines (Mode 2 TIFF). Picture switching techniques. Printer driver code for printing mono/colour images. Sorting algorithms. Using the BitBlit routines. Code for using the File Selector. ---------------------------------------------------------------------- EDITORIAL ========= ARTICLES -------- We still need some articles for the June issue (and beyond) so if you can provide something, please do or we will have to rely on PD material for future issues. We would also like to see more programming questions asked (and answered, of course) so please send them in. GEM TUTORIALS ------------- In issues 3 to 7 we published the GEM tutorial series (parts 1-10) by Tim Oren (who wrote GEM). We have now got parts 11 to 17 which we will be starting to publish next month. In part 17 there is a reference to a part 18 which we do not have. If anyone with Internet access can find this one or any later ones perhaps they could send them on to us. The filenames used are GMCL11.DOC to GMCL17.DOC for the parts that we have already. ---------------------------------------------------------------------- CORRESPONDENCE ============== To: Geoff Smith From: Mrten Lindstrm You got the warning "opcode generated for MC68020/MC68030/MC68040" regarding the assembler instruction: MOVE.B (A1,D1.W),D0 Try changing this into MOVE.B 0(A1,D1.W),D0 i.e. insert a '0' before the parenthesis. To be honest I am surprised that Lattice C (which I don't have myself) complains about the first expression. It is true that this assembler syntax is strictly correct only with 68020+ processors, but it should assemble identically for them, I think, as the latter 68000 line. And e.g. Devpac (both 2 and 3) accepts the former line without complaining even when set to generate pure 68000 code. For someone who doesn't know any assembler: What the instruction does is to interpret the sum of register A1 (long) + lower word of D1 (+ 0) as an address in memory from which a byte is read and copied into (lowest byte of) register D0. To: Steven Gale (and others) THE GEM CLIPBOARD AND OTHER WAYS TO TRANSFER DATA BETWEEN PROGRAMS ------------------------------------------------------------------ The GEM clipboard became official Atari standard a long time after most of the ST documentation, still floating around, was written. Which is probably why you haven't been able to find anything about it. The clipboard itself is really a folder on disk, the path of which can be found with the AES call SCRP_READ or, if not already set, set with SCRP_WRITE. Applications supporting it typically do this within the context of Macintosh style block editing functions, 'Cut', 'Copy' and 'Paste' (under an 'Edit' menu). The presence of these functions is no guarantee of clipboard support however (e.g. Devpac 3 which uses an internal buffer instead). NOTE: A program that, like Devpac 3, uses an internal buffer for Cut, Copy and Paste should ideally (though Devpac doesn't) have some means - e.g. an extra, checkable, menu item 'Use Clipboard' - to toggle between GEM clipboard and the internal buffer. Standard keyboard short-cuts, according to both AC and German sources, are (sadly not adhered to by Devpac): Cut: Ctrl-X Copy: Ctrl-C Paste: Ctrl-V If the menu item 'Copy' is used then the marked block of text or image or whatever should simply be written to the clipboard. 'Cut' should in addition delete the block within the edited document in the program. 'Paste' should cause the contents of the clipboard to be read and inserted into the edited document (at the position of the cursor or equivalent). When there isn't a block selected in the program, then 'Copy' and 'Cut' should ideally be disabled. You might think that 'Paste' should be similarly disabled when the clipboard is empty, but alas there is no standard way, that I know of, to tell when the clipboard becomes updated. (If for some reason you don't use a menu in your program - e.g. in an accessory where this isn't easy to implement - I think it should be quite alright to use the clipboard anyway. E.g. accessed via dialogue buttons or just with the above keyboard short-cuts.) To write to the clipboard: 1) Call SCRP_READ to get the path to the clipboard. If the returned string is empty, no path has ever been defined since bootup. If so, create a directory \CLIPBRD - on drive C: if there is one, otherwise on drive A: (or you can ask the user). I suppose you could alternatively read _bootdev, i.e. the word at $446 (in supervisor mode) to get the boot drive (0=A:, 2=C: etc.). Tell the world about the new clipboard by calling SCRP_WRITE; the path given with this should be on the format 'C:\CLIPBRD\'. 2) Delete all files SCRAP.* within this folder. 3) Write one or more files named SCRAP.*, where * is a standard file name extension describing the file format (see below). IMPORTANT: If you write more than one file, each of them should contain essentially the same information, though in different formats, since the idea is that an importing program should only have to choose one of them. To read from the clipboard: 1) Call SCRP_READ to get the path to the clipboard. If the returned string is empty (null) there is of course nothing to read. 2) Search the clipboard path for files named SCRAP.* with suitable extensions - corresponding to file formats that your program supports. Pick the one of your choice (probably the one of the richest format that your program can digest) and load it. Note that the correct format for the scrap path string is 'C:\CLIPBRD\' (with an ending \) but the Atari Compendium warns that some programs set it wrongly (probably either leaving out the ending \ or else adding a filename 'SCRAP.*'), so some flexibility when parsing it may be an advantage. File extensions and corresponding formats are (according to Julian Reschke - ST Magazin 9/89 - the personal efforts of whom probably did a great deal to make the clipboard into official Atari standard): TXT ASCII text where each line is ended with CR/LF (though a reader should never count on the CR, but be prepared for Unix style line ends with only LF). Any space at the end of a line is to be interpreted as a soft carriage return (i.e. reformatable). (Nothing is said though, about hyphens at the end of lines.) ASC ASCII text where only paragraph ends are marked with CR/LF. (I.e. soft carriage returns aren't marked at all). 1WP First Word Plus WP Word Perfect TEX TeX RTF Rich Text Format EPS Encapsulated Postscript Others are, of course, possible. Unfortunately there probably isn't a an extension defined for Protext format files, considering that Protext isn't much of a GEM program, but maybe we could just ask Arnor to decide on one? (Perhaps 'PTX' would be suitable?) For picture files I think the extensions are obvious, and the same is probably true for most other types of data. As a recommended minimum, exported text should always be written to a SCRAP.TXT file at least and images probably always to a SCRAP.IMG file (and/or SCRAP.GEM for object graphics). Some may want a more direct data exchange, memory to memory without going via the disk based clipboard. Unfortunately there isn't a standard method defined for this, usable with all programs and OS- versions. Specifically there IS an official 'Drag&Drop' protocol which requires MultiTOS, and there are a number of unofficial protocols defined for specific programs (e.g. Easy Draw and Gemini) which however will probably NOT work with MultiTOS. The natural point to start from is in all cases with the AES message pipe. With APPL_WRITE you can send 16-byte messages to any other GEM program, including accessories, which gets them the same way it gets messages from the system, i.e. waiting for them through a call of EVNT_MULTI or EVNT_MESAG. 16 bytes isn't much however and the first 6 of these are in addition reserved for special purposes; (word0: message type ID, word1: application ID of sender, word2: 0 - or extra length of message). In theory you can send longer messages as well, which could then be read by APPL_READ, but from what I have heard the AES message system is so bug-ridden that APPL_READ really is best forgotten, and the message length at all times kept to 16 bytes to be received by the EVNT functions only. (As a proof of this Atari themselves chose to use MiNT pipes for their Drag&Drop protocol rather than fix the existing AES message system.) MEMORY POINTER PASSING: ----------------------- The 16 (or effectively 10) bytes of an AES message may not be much but it IS more than enough to pass a pointer to some memory block where more can be found, and this is how it is done I think by programs such as Easy Draw, Gemini etc. (possibly also by K-Word/K-Roget asked about by Peter Hibbs). It may even be possible to define something similar to the MultiTOS Drag&Drop protocol, but based on pointer passing instead of MiNT pipes. There certainly would be use for such a protocol, e.g. as a backup when MiNT pipes aren't available. (Though one complication is admittedly that the basic AES of most STs and TTs lacks the means to conveniently determine application IDs for window owners or other programs.) The first problem with the protocols of these programs is that they aren't as widely known as they should be to become real standards. You need to know message type IDs and structures and whether a receiver is required to send some reply/acknowledgement etc. (I for one don't know any of these protocols and would be very grateful indeed for details.) IF ANYONE KNOWS OF A GOOD (AND FAIRLY WIDESPREAD) PROTOCOL I THINK WE IN ICTARI COULD MAKE AN ACTIVE EFFORT TO SPREAD IT FURTHER! This is in my eyes the ideal kind of task for a programmers group such as Ictari. The second problem is that programs under MultiTOS are not usually meant to access each others memory areas, and the 68030 processor in Falcons and TTs can physically enforce that this doesn't happen (e.g. 'logical addresses' means that different programs may see different things on the 'same' address). Which probably means you HAVE to use the ordinary Drag&Drop protocol (below) when under MultiTOS. THE DRAG&DROP PROTOCOL OF MULTITOS ---------------------------------- This, like the clipboard, is official standard which I can describe fully (drawing on the Atari Compendium and on Julian Reschke of ST Magazin/ST Computer; I haven't been able to test it myself). The Drag&Drop protocol however requires the pipelining of MiNT (MultiTOS), which means it won't work on many present systems. Like with memory pointer passing, the initial contact is made through a 16-byte AES message sent with APPL_WRITE. The rest of the communica- tion takes place in a MiNT pipe. From a user's point of view, the Drag&Drop protocol allows him to select a piece of data within the window of one program, drag it with the mouse to the window of another program and drop it there. The AP_DRAGDROP message: The structure of the AES message, to be sent with APPL_WRITE to the program owning the destination window, is as follows: W: 63 (AP_DRAGDROP) W: application ID of sender W: 0 (No extra length of this message; just 16 bytes) W: Window handle of destination W: Mouse X position of drop (probably got from an EVNT call) W: Mouse Y position of drop - " - W: Keyboard shift status at drop - " - 2B: The 2-character file extension of the MiNT pipe 16 bytes in all. NOTE: -1 can be used in place of a window handle if the destination is a program in general and no particular window. The recipient is then usually expected to open a new window for the dropped data. Receiving: The 'recipient' should do the following as soon as it gets (through EVNT_MULTI or EVNT_MESAG) an AES message of type 63 (AP_DRAGDROP): 1) Open the 'file' (actually MiNT pipe) 'U:\PIPE\DRAGDROP.xx' where 'xx' are the two last bytes of the 16-byte Drag&Drop message. You open it as an ordinary file with FOPEN. 2) Write one byte to the pipe (with FWRITE): =0 if you are ready to receive (in this window). =1 otherwise. Note that even a program that doesn't otherwise support the Drag&Drop protocol could at least write a 1 byte, since that will spare the user having to wait for timeout (3 or 4 seconds). If you write a 1 then immediately close the pipe (with FCLOSE). 3) If you write a 0, you should follow this with 32 further bytes. These should be 8 4-byte file extensions (e.g. '.IMG', '.TXT') corresponding to file formats that your program recognizes and in order of preference. (Compare with the GEM clipboard above.) If you recognize less than 8 formats then write null bytes for the ending ones. 4) Read (with FREAD) one word from the pipe giving the length of a following header. Then read (again with FREAD) the header itself: 4B: Format of the data to be transferred (e.g. '.TXT') L: Length of the data ?B: Null-ended name of data chunk (to label an icon for it) ?B: Null-ended name for an associated file ?B: Possible extra info not yet defined (read it and ignore) Both data name and filename can be left out (= only one null byte each). NOTE: If the return value from FREAD is 0 (zero bytes read) this means that the sender - 'originator' - has given up and closed the pipe. You should then do the same at your end of it (use FCLOSE). 5) Write one byte = 0: if OK, then read as many bytes as length of data and close. 1: to abort instantly, then close. 2: if format not acceptable, then go back to step 4 above. 3: if data too long, then go back to step 4 above. 4, 5 or 6 then close, if drop was on a 'trashcan', 'printer' or 'clipboard' respectively. Sending: Whenever the user has selected and dragged something, a program should of course 'keep an eye' on the mouse. (Waiting for a mouse button release with EVNT_MULTI.) 1) When the user releases the mouse button, call WIND_FIND to get the handle of the window where the drop was made. Then convert this into the ID of the owning program via a call of WIND_GET, mode 20 (this wind_get mode doesn't exist in the basic ST and TT AES versions but should be there in the AES of MultiTOS - and Falcon). If it's the window of another program then proceed. 2) Use the MiNT call PSIGNAL ('GEMDOS number' 274) to make any SIGPIPE (13) message ignored. I.e. call Psignal(W:13,L:1). This is to avoid your poor little program getting killed just because no-one is reading its pipe. 3) Create the 'file' (actually MiNT pipe): 'U:\PIPE\DRAGDROP.xx' where 'xx' could be any two ASCII characters usable in file names. (The Atari Compendium proposes that you try your application ID in ASCII characters - e.g. '03' if your ID is 3.) If you fail, you can just try two other ASCII characters until you succeed. You use the ordinary FCREATE call (GEMDOS 60) AND MAKE SURE THE 'HIDDEN BIT' IS SET - i.e. the second parameter 'attribute' = 2. (With pipes this bit just causes reads to return EndOfFile when the other end of the pipe has been closed.) 4) Now it is time to tell the recipient about the drop and pipe by sending the type 63 (AP_DRAGDROP) AES message with APPL_WRITE. See above. 5) Use the MiNT call FSELECT (GEMDOS 285) to wait for the pipe being ready for reading (meaning that the recipient has written something) or a timeout of 3-4 seconds. The call should look something like FSELECT(L:3500,L:rfds,L:0,L:0) for a timeout of 3.5 seconds, where rfds points to a longword with only the one bit set, the number of which = file handle of pipe. If FSELECT returns 0 then timeout was reached (close the pipe and abort), otherwise contact has been made. 6) Read one byte from the pipe (with FREAD). This should be either 0 (= OK, carry on) or 1 (= Abort). In the latter case immediately close the pipe with FCLOSE. 7) If the byte read was 0, then read 32 further bytes - the recipient's wish list of formats described above (recipient's step 3). 8) Decide on a format to send. This doesn't HAVE to be a format favoured (or even mentioned) by the recipient. The originator is free to have its views too and should the recipient refuse to accept the decision, the originator can change it later. Write the following structure to the pipe: W: Length of the following header (not including this word) 4B: Format of the data to be sent (e.g. '.TXT') L: Length of the data ?B: Null-ended name of data chunk (or just one null) ?B: Null-ended name for an associated file (or just one null) 9) Read the one new reply byte from the recipient. If this is- 0: Write the data to the pipe (FWRITE) and then close it (FCLOSE). 1: Close the pipe instantly 2: Try another format (repeat step 8) or close the pipe. 3: (Too much data) Try smaller format or close. 4: (Drop on trashcan) Close pipe and delete data within program. 5: (Drop on printer) Close pipe and print data. 6: (Drop on clipboard) Close and treat like 'Copy' to clipboard. ARGS and PATH: In place of data format specifiers ('.IMG', '.TXT' etc.) 'ARGS' or 'PATH' (no leading stop) could be used. Both of them seem to be primarily intended for communication with a Desktop or similar program. ARGS is simply a command line, i.e. one or more parameters (usually file specifications) separated by spaces. Note however that spaces within a parameter are allowed by enclosing it within single quotes. For an actual single quote character it has to be doubled (''). ARGS data are transferred exactly like other types of data. (Sent by the Desktop to a program into whose window the user has dropped file icons.) PATH on the other hand REVERSES THE DATA TRANSFER. When the originator decides that 'PATH' data are to be sent, it is really a request that the recipient sends the path corresponding to the receiving window. A recipient that accepts to that with a null byte, should make sure this is immediately followed by the path - null-ended. The data length given by the originator should be interpreted merely as a maximum. NOTE: The last character of the path, before the null, should be a '\' if the window corresponds to a directory. Should the window correspond to a file then the filename should of course be included. (If the destination is a directory I presume that the originator will follow the Drag&Drop PATH information exchange with a simple file copy operation to it.) To: Peter Hibbs Ictari proposals for Devpac improvements A very good initiative this is and here are my views: 1) Bug in Mon 3.10: The Ctrl-O option, to switch between colour resolutions, seems to usually crash the ST (except when nothing has been loaded). 2) The DC directive: With e.g. DC.B Devpac lets all values in the range {-256:255} pass without error messages. But only values in the range {-128:255} could of course possible have been intended. (A value of -256 is assembled by Devpac as 0, -255 as 1 etc.) The corresponding thing applies for word and long values. This bug annoyed me in Devpac2 and is still there in Devpac3. Also, Easy Rider (German assembler by Andreas Borchard) offers an additional directive DSC (where the S stands for Signed) which is the same as DC but narrowing further the allowed range of values. E.g. for byte: values {-128:127} and for word: {-32768:32767}. This is very handy for address offsets and I would like to see it in Devpac too. 3) More options for STRING HANDLING please. To enable the writing of more powerful macros. a) In Easy Rider you can set a symbol to a string with SETSTR. b) Likewise in Easy Rider there are operations to extract characters from strings, concatenate strings and to determine string length and the character position within a string of another string (1=begins at first character, 0=not found). There is even a specialized operation to determine the parameter position of a named (macro) parameter within a parameter list - both given as strings. This all makes it much easier to write powerful macros in general (intelligently analysing their input), and can also be used for special purposes such as encryption macros, taking strings as input and producing - at assembly time - something that is unreadable with a mere disk editor or similar. (Easy Rider uses $( ) to enclose strings in expressions - e.g. $(string). 1:$(string) extracts the first character of the string - 's' in the example. $(str):$(string) finds the position within 'string' where 'str' is found - =1 in the example.) 4) Please implement the BETTER OPTIMIZATION OPTIONS of Easy Rider: - Instead of the Devpac O12+, which can optimize "MOVE.L #x,An" to "MOVE.W #x,An", Easy Rider offers the more powerful change to "LEA x,An" which can then immediately be further optimized to either short absolute OR PC-relative addressing. - Similarly Easy Rider can change "MOVE.L #x,-(SP)" into "PEA x" to be possibly further optimized. - The E.R. optimization "MOVE #0,ea" to "CLR ea" may be less obviously useful; whoever would be stupid enough to write 'move #0,ea' rather than 'clr ea'? The answer is: a macro! And I think it is with macros that optimization is the most useful anyway. - Also, the E.R. MOVEQ optimization will work not only on L sized MOVE instructions but on W sized ones as well, IF no explicit '.W' has been used. This admittedly may be more a matter of personal taste, though I find it, again, to be useful within macros. - Finally E.R. can, if you want to, optimize branches forward as well as backwards to short (though it of course makes the assembly take a longer time since it has to be redone - possibly several times). 5) Continue the Devpac integration (of assembler, editor and debugger) by incorporating the LINKER as well. In Easy Rider (which otherwise lacks in integration) you can use the INCLUDE directive with object files just as easily as with source files. As it is now I doubt that I will ever use the Devpac linker since that would have me leave the integrated environment of Devpac. ---------------------------------------------------------------------- To: Mrten Lindstrm From: Mark Baker " This means that I for instance can write my name 'Mrten Lindstrm' with Swedish letters, and feel confident, I think, that it will come up right not just on any Atari computer - regardless of its nationality - but on any PC compatible too (as long as the standard character set for these computers is used). " It works on a PC running DOS. Windows programs use an ISO standard character set (Americans seem to be under the impression that ANSI is a world standards organisation and not simply a national member of the ISO, so they incorrectly refer to ANSI C and the ANSI character set) " The double s character of German is another problem. Atari computers reserve character number 158 for this, but still use a symbol identical to Greek beta (#225). On PC:s (and printers) character 158 is a Pt character (Pesetas), and Protext simply replaces Greek beta with German double s. (ST Protext translates any 158 into 225.) " Many Germans seem to be unaware that esszet (sp?) and beta are different and assume the button on their keyboard is a beta character. This looks strange in different fonts that do differentiate between them, and sometimes get automatically converted to ss in emails! " Yes, the keyboards ARE different between different nationalities! I send in a file (KBD_MAPS) with keyboard maps for some nationalities, which I hope could be extended by people who know more. " There should be no need for a programmer to know the keyboard map. " 2) If you do place commands on the main keypad, and you document the commands as bound to keys with certain characters on them, then you normally have to check input ASCII NUMBERS RATHER THAN SCAN CODES. " For alt shortcuts you need something like: keytable = Keytbl( (char *)-1, (char *)-1, (char *)-1 ) ; ascii = *(*keytable + scan ) ; Atari's style guides tell you not to use alt shortcuts, but I, like most other people, do because there aren't enough ctrl shortcuts and most of them have defined meanings. " Should you put TWO functions on the same key, however, things become a bit more problematic. A bad example is in Devpac 3, Mon, where . and , are documented as 'Next/Previous View' while Shift-. is said to 'Open View' and Shift-, to 'Close View'. The key [I had to press] has the < and > on it, and is on my Swedish keyboard, as I said, different from the , and . keys (but not on the British one, I know). Bad documentation/programming that is. (Even the masters aren't infallible.) So, how should Hisoft have done it? " Well, they could have just put < and > in the menu. But in general it is not recommended to use shift and a punctuation symbol. " One simple solution would have been to not use keys on the main keypad at all, but to use some function keys. (I am surprised that none of these have been used in Mon.) " I prefer to have function keys set up by the user. " Another solution, of course, would have been to document 'Open View' as '>' and 'Close View' as '<'. But this wouldn't perhaps look as neat. Apart from letters and keys, I think this is what could cause the most confusion with the user. If your program prints dates for any reason .." I normally get and display dates in the form 24 Mar 95; this is the normal way for most Europeans and using the name of the month means Americans aren't confused (where they would be by 24/03/95) but I believe this could still confuse people from some Scandinavian countries, who use year/month/day, is this true? To: Jonathan White " 0 No icon 1 A diamond with an exclamation mark (Note only) 2 Point down triangle with a question mark (warning) 3 A Hexagon with 'STOP' inside it (are you sure???) " Apparently AES 4 adds a couple more, an i for information and a disc. I don't suggest anyone uses them as on older AES versions you will get various bits of junk including the mouse pointers. " Note Icon 1 is usually meant for note messages you simply have to acknowledge - this program won't run | in less than one meg {OK} " According to Atari's style guide this is for errors or conditions requiring immediate action. I have in the past used it for program credits etc, but apparently you should use the i or no icon at all for this. " technically against Atari user interface guidelines (which state buttons should go OK CANCEL OTHERS) " Actually they state that it should be OK Cancel Others ie. mixed case for all except OK. */ Actually we may have capitalized those words to make them stand out better but as we don't have the original copy we can't be sure. If we did we apologise to Mrten. ICTARI /* " The first step is to call form_center() to set the point on the screen you wish a particular dialog box to appear. Then you call form_dial() with the FMD_START parameter to tell the system you are drawing on the screen and not to do anything while you do so. Then you use objc_draw() to actually display the dialog on the screen. " You also need wind_update() This is not mentioned in the manual for Lattice C but apparently it has been documented by Atari all along; if you don't have it your dialogue will in some circumstances be messed up in a multitasking system. " Note that because form_dial() intercepts all inputs via an evnt_multi() inside it, while you have a dialog on the screen, the rest of your application, and any other applications in a non-preemptive multitasking environment (errr.. Geneva is the only one for the Atari, as far as I know) will be frozen. In MultiTOS, the programs continue running, but the screen is not updated outside your dialog. " evnt_multi() does NOT stop the other applications running. In fact it allows them to, you must have an event library call if you want other apps to run under Geneva (have I said this before?). The wind_update() call stops other apps from displaying or receiving input and there is one built in to form_do(). " First off, define your pointer as OBJECT *(something) Second, use rsrc_gaddr to point the pointer at the root object of the tree by using rsrc_gaddr(R_TREE, NAME_OF_ROOT_OBJECT, &(something)) " ^^^^^^^^^^^^^^^^^^^ It should be the name of the tree which is not necessarily the same as the name of the root object. To: Mrten Lindstrm " of it being too basic). Here is one concerning a relatively basic and fundamental issue: - whether or not MALLOC (GEMDOS 72) could return memory blocks on odd addresses. Have I missed an official statement about this? Was there ever an early TOS version returning odd memory block addresses, or was it just a false alarm from the start (not unusual in old Atari documentation)? " I don't think it was specified either way. Perhaps your book may have read this to mean that any addresses were possible where actually Atari just forgot to document that it always returns even addresses. As old TOS versions return even addresses and they couldn't change it now without breaking too much code - and why should they change it anyway? - I think you're safe assuming it to be even. To: Peter Hibbs " While on the subject of HiSoft's assembler I think that, although it is very good at assembling, the source code editor itself could still be improved. For example, I would like to see an option to export a highlighted block to the 'clipboard' so that the marked text could be copied into another program such as Harlekin, for example. I would like to see the first few characters of each 'bookmark' location displayed in the information line of the window so that I can remember which 'bookmark' is which. I would like to have a 'move block' option where a block of text can be moved in one operation rather than having to 'copy' and then 'delete'. I could probably think of a few more as well if pushed. " I'm sure I could add a few to that list. For example the project files used in Lattice C could be improved greatly, (although the otherwise almost identical editor supplied with Devpac unfortunately lacks that facility altogether) " If anyone else has any other additions they would like to see, please send them in and perhaps I will send them all to HiSoft. They might even implement them if they come from a programmers group, well you never know ! " They've apparently stopped developing for the Atari (and for the Amiga as well???) although they're still selling stuff. To: Jim Taylor " To complement the Bezier line drawing routines in my CAD application I would like some information/C code on Spline curve generation. " I can explain exactly what a spline is; I've got information in a textbook about how to calculate it. I can send you a photocopy if you want but I warn you it is difficult (I've got maths A levels at grade A and I can't understand it, if I could I'd have explained it here). A spline is a piecewise polynomial interpolation, that is there is a polynomial (normally a cubic) between each point. Unlike using a single polynomial for all points this method doesn't oscillate between points. The polynomials are chosen so that the spline is several times differentiable at the nodes (fixed points). That is there are no sudden jumps in the spline itself or in it's gradient. To: Steve Gale " I am writing a program in which I would like to transfer data to or from other programs via the system 'clipboard' but there does not seem to be any information available about this. Is there any standard format for this transfer, is the data stored in RAM or on disk, etc. " It's stored on disc, in any format used for disc files, eg. .IMG for bitmaps, .GEM for vector graphics, .TXT or .RTF for text etc. You can have multiple formats so your own application can get more information from its own format while other apps can read the standard format. " Does anyone have any information on this subject ? " I might send an article to Ictari about this. */ Please do. ICTARI /* To: Tony Harris "I know there are ways of avoiding some of the standard GEMDOS calls, that is by going directly to the hardware, but is there any way of doing this for the file calls, ie, f_open, f_read and f_close?" Not really. You can go directly to the disc hardware, but this differs depending on what device it is so it is definitely not recommended. The bios Rwabs() call avoids using GEMDOS but this too is not recommended as it doesn't understand file systems and treats the disc as a series of sectors: if you want to access files on the disc you need to use GEMDOS. The bios call is useful for writing a sector editor but that's about it. " Is there a way of preventing the mouse from entering part of the screen ? i.e. to prevent corruption when redrawing. At the moment I turn off the mouse if it strays into the barred area, but what I really want is the effect of not being able to go any further. " Not easily, without hooking into a timer interrupt and repositioning the mouse if necessary. And even then it could possibly move and destroy your image before your interrupt runs. To: Lee Russell " I am part way through writing a program to crop .IMG format pictures. Has anybody got some 'C' code they want to donate to save a block of memory in that format? " I've got code for uncompressed .IMG files; if anyone has code for writing compressed files I'd be interested in that myself. ---------------------------------------------------------------------- To: ICTARI From: Iain Mcleod Is ICTARI going to be on the Internet? (WWW). If it is/going to be, could you inform me of its address. */ We do not have an Internet address at present although there would be some obvious advantages if we did. We will investigate the possibility of joining although the costs of purchasing a high speed Modem and membership fees may be the deciding factor. We do have a number of members who have access to the Internet and we hope that they will look out for any PD articles/software which would be of interest to programmers and send it on to us. Perhaps these members could also let us know how to join the Internet, what software and hardware would be required and how much it all costs. ICTARI /* ---------------------------------------------------------------------- To: *.* From: Peter Hibbs Is there any 'legal' way to determine if a menu has been dropped down. In my current project I am using 'evnt_multi' with a zero timer event to flash a cursor, etc. When the user moves the mouse into the menu bar I need to stop the cursor flashing underneath the menu options. Unfortunately, even though the AES is displaying the drop down menu items, the timer event is still active and the cursor continues to flash. To get round this I have defined a 'rectangle evnt' which covers the area of the menu bar so that when the mouse moves into the menu the rectangle event code switches off the cursor. I also had to add another test in the timer event code to monitor the x and y co- ordinates of the mouse to do the same test because if the mouse was moved too quickly into the menu bar, the rectangle event did not catch it. Fortunately the rectangle event function is disabled by the AES when a menu is dropped down so that even though the mouse is moved down into the main screen area as the menu items are hilighted/unhighlighted, the rectangle event code does not signal that the mouse has moved outside the defined rectangle (which would then re-enable the cursor). While this system does seem to work quite well it does seem a bit messy. Is there a simpler way to do this? Does anyone know how the Canon Bubble Jet printers print out bit map graphics images? Any information would be very welcome. I have just purchased an Autoswitch Overscan unit for my STFM (but not fitted yet) to give me a larger screen area. Does anyone have any tips on how to write software which will still work properly on larger size screens. Obviously the screen width and height can be found from the v_opnwk VDI function but looking through the manual there seems to be a number of other things which also need to be taken into consideration. The manual does give some guidance for programmers but it is very brief and written in broken English (and even lapses into German a couple of times). Has anyone had any practical experience of writing software for larger screens, if so perhaps they would like to let us know how to implement such code. --------------------------- End of file ------------------------------ of file ------------------------------ j .; 4 1_=dž@kRRL< q|;).~8===8?0@>?r| >?)6N?7ԨWl>l`:`?? ^}?t  {?@]>=???q`@~__<~jw? Zg_`ƀ(  ???Þ=. O## }y2{KH~/f,~????C_`O||?o0|@jv/i^a8/4 0'???\?r#vr> l{& h""   <. e.. FAQ e. e.. eC FAQ QfArchive-name: C-faq/faq Comp-lang-c-archive-name: C-FAQ-list [Last modified April 16, 1994 by scs.] Certain topics come up again and again on this newsgroup. They are good questions, and the answers may not be immediately obvious, but each time they recur, much net bandwidth and reader time is wasted on repetitive responses, and on tedious corrections to the incorrect answers which are inevitably posted. This article, which is posted monthly, attempts to answer these common questions definitively and succinctly, so that net discussion can move on to more constructive topics without continual regression to first principles. No mere newsgroup article can substitute for thoughtful perusal of a full-length tutorial or language reference manual. Anyone interested enough in C to be following this newsgroup should also be interested enough to read and study one or more such manuals, preferably several times. Some C books and compiler manuals are unfortunately inadequate; a few even perpetuate some of the myths which this article attempts to refute. Several noteworthy books on C are listed in this article's bibliography. Many of the questions and answers are cross-referenced to these books, for further study by the interested and dedicated reader (but beware of ANSI vs. ISO C Standard section numbers; see question 5.1). If you have a question about C which is not answered in this article, first try to answer it by checking a few of the referenced books, or by asking knowledgeable colleagues, before posing your question to the net at large. There are many people on the net who are happy to answer questions, but the volume of repetitive answers posted to one question, as well as the growing number of questions as the net attracts more readers, can become oppressive. If you have questions or comments prompted by this article, please reply by mail rather than following up -- this article is meant to decrease net traffic, not increase it. Besides listing frequently-asked questions, this article also summarizes frequently-posted answers. Even if you know all the answers, it's worth skimming through this list once in a while, so that when you see one of its questions unwittingly posted, you won't have to waste time answering. This article is always being improved. Your input is welcomed. Send your comments to scs@eskimo.com . The questions answered here are divided into several categories: 1. Null Pointers 2. Arrays and Pointers 3. Memory Allocation 4. Expressions 5. ANSI C 6. C Preprocessor 7. Variable-Length Argument Lists 8. Boolean Expressions and Variables 9. Structs, Enums, and Unions 10. Declarations 11. Stdio 12. Library Subroutines 13. Lint 14. Style 15. Floating Point 16. System Dependencies 17. Miscellaneous (Fortran to C converters, YACC grammars, etc.) Herewith, some frequently-asked questions and their answers: Section 1. Null Pointers 1.1: What is this infamous null pointer, anyway? A: The language definition states that for each pointer type, there is a special value -- the "null pointer" -- which is distinguishable from all other pointer values and which is not the address of any object or function. That is, the address-of operator & will never yield a null pointer, nor will a successful call to malloc. (malloc returns a null pointer when it fails, and this is a typical use of null pointers: as a "special" pointer value with some other meaning, usually "not allocated" or "not pointing anywhere yet.") A null pointer is conceptually different from an uninitialized pointer. A null pointer is known not to point to any object; an uninitialized pointer might point anywhere. See also questions 3.1, 3.13, and 17.1. As mentioned in the definition above, there is a null pointer for each pointer type, and the internal values of null pointers for different types may be different. Although programmers need not know the internal values, the compiler must always be informed which type of null pointer is required, so it can make the distinction if necessary (see below). References: K&R I Sec. 5.4 pp. 97-8; K&R II Sec. 5.4 p. 102; H&S Sec. 5.3 p. 91; ANSI Sec. 3.2.2.3 p. 38. 1.2: How do I "get" a null pointer in my programs? A: According to the language definition, a constant 0 in a pointer context is converted into a null pointer at compile time. That is, in an initialization, assignment, or comparison when one side is a variable or expression of pointer type, the compiler can tell that a constant 0 on the other side requests a null pointer, and generate the correctly-typed null pointer value. Therefore, the following fragments are perfectly legal: char *p = 0; if(p != 0) However, an argument being passed to a function is not necessarily recognizable as a pointer context, and the compiler may not be able to tell that an unadorned 0 "means" a null pointer. For instance, the Unix system call "execl" takes a variable-length, null-pointer-terminated list of character pointer arguments. To generate a null pointer in a function call context, an explicit cast is typically required, to force the 0 to be in a pointer context: execl("/bin/sh", "sh", "-c", "ls", (char *)0); If the (char *) cast were omitted, the compiler would not know to pass a null pointer, and would pass an integer 0 instead. (Note that many Unix manuals get this example wrong.) When function prototypes are in scope, argument passing becomes an "assignment context," and most casts may safely be omitted, since the prototype tells the compiler that a pointer is required, and of which type, enabling it to correctly convert unadorned 0's. Function prototypes cannot provide the types for variable arguments in variable-length argument lists, however, so explicit casts are still required for those arguments. It is safest always to cast null pointer function arguments, to guard against varargs functions or those without prototypes, to allow interim use of non-ANSI compilers, and to demonstrate that you know what you are doing. (Incidentally, it's also a simpler rule to remember.) Summary: Unadorned 0 okay: Explicit cast required: initialization function call, no prototype in scope assignment variable argument in comparison varargs function call function call, prototype in scope, fixed argument References: K&R I Sec. A7.7 p. 190, Sec. A7.14 p. 192; K&R II Sec. A7.10 p. 207, Sec. A7.17 p. 209; H&S Sec. 4.6.3 p. 72; ANSI Sec. 3.2.2.3 . 1.3: What is NULL and how is it #defined? A: As a matter of style, many people prefer not to have unadorned 0's scattered throughout their programs. For this reason, the preprocessor macro NULL is #defined (by or ), with value 0 (or (void *)0, about which more later). A programmer who wishes to make explicit the distinction between 0 the integer and 0 the null pointer can then use NULL whenever a null pointer is required. This is a stylistic convention only; the preprocessor turns NULL back to 0 which is then recognized by the compiler (in pointer contexts) as before. In particular, a cast may still be necessary before NULL (as before 0) in a function call argument. (The table under question 1.2 above applies for NULL as well as 0.) NULL should _only_ be used for pointers; see question 1.8. References: K&R I Sec. 5.4 pp. 97-8; K&R II Sec. 5.4 p. 102; H&S Sec. 13.1 p. 283; ANSI Sec. 4.1.5 p. 99, Sec. 3.2.2.3 p. 38, Rationale Sec. 4.1.5 p. 74. 1.4: How should NULL be #defined on a machine which uses a nonzero bit pattern as the internal representation of a null pointer? A: Programmers should never need to know the internal representation(s) of null pointers, because they are normally taken care of by the compiler. If a machine uses a nonzero bit pattern for null pointers, it is the compiler's responsibility to generate it when the programmer requests, by writing "0" or "NULL," a null pointer. Therefore, #defining NULL as 0 on a machine for which internal null pointers are nonzero is as valid as on any other, because the compiler must (and can) still generate the machine's correct null pointers in response to unadorned 0's seen in pointer contexts. 1.5: If NULL were defined as follows: #define NULL ((char *)0) wouldn't that make function calls which pass an uncast NULL work? A: Not in general. The problem is that there are machines which use different internal representations for pointers to different types of data. The suggested #definition would make uncast NULL arguments to functions expecting pointers to characters to work correctly, but pointer arguments to other types would still be problematical, and legal constructions such as FILE *fp = NULL; could fail. Nevertheless, ANSI C allows the alternate #define NULL ((void *)0) definition for NULL. Besides helping incorrect programs to work (but only on machines with homogeneous pointers, thus questionably valid assistance) this definition may catch programs which use NULL incorrectly (e.g. when the ASCII NUL character was really intended; see question 1.8). References: ANSI Rationale Sec. 4.1.5 p. 74. 1.6: I use the preprocessor macro #define Nullptr(type) (type *)0 to help me build null pointers of the correct type. A: This trick, though popular in some circles, does not buy much. It is not needed in assignments and comparisons; see question 1.2. It does not even save keystrokes. Its use suggests to the reader that the author is shaky on the subject of null pointers, and requires the reader to check the #definition of the macro, its invocations, and _all_ other pointer usages much more carefully. See also question 8.1. 1.7: Is the abbreviated pointer comparison "if(p)" to test for non- null pointers valid? What if the internal representation for null pointers is nonzero? A: When C requires the boolean value of an expression (in the if, while, for, and do statements, and with the &&, ||, !, and ?: operators), a false value is produced when the expression compares equal to zero, and a true value otherwise. That is, whenever one writes if(expr) where "expr" is any expression at all, the compiler essentially acts as if it had been written as if(expr != 0) Substituting the trivial pointer expression "p" for "expr," we have if(p) is equivalent to if(p != 0) and this is a comparison context, so the compiler can tell that the (implicit) 0 is a null pointer, and use the correct value. There is no trickery involved here; compilers do work this way, and generate identical code for both statements. The internal representation of a pointer does _not_ matter. The boolean negation operator, !, can be described as follows: !expr is essentially equivalent to expr?0:1 It is left as an exercise for the reader to show that if(!p) is equivalent to if(p == 0) "Abbreviations" such as if(p), though perfectly legal, are considered by some to be bad style. See also question 8.2. References: K&R II Sec. A7.4.7 p. 204; H&S Sec. 5.3 p. 91; ANSI Secs. 3.3.3.3, 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, and 3.6.5 . 1.8: If "NULL" and "0" are equivalent, which should I use? A: Many programmers believe that "NULL" should be used in all pointer contexts, as a reminder that the value is to be thought of as a pointer. Others feel that the confusion surrounding "NULL" and "0" is only compounded by hiding "0" behind a #definition, and prefer to use unadorned "0" instead. There is no one right answer. C programmers must understand that "NULL" and "0" are interchangeable and that an uncast "0" is perfectly acceptable in initialization, assignment, and comparison contexts. Any usage of "NULL" (as opposed to "0") should be considered a gentle reminder that a pointer is involved; programmers should not depend on it (either for their own understanding or the compiler's) for distinguishing pointer 0's from integer 0's. NULL should _not_ be used when another kind of 0 is required, even though it might work, because doing so sends the wrong stylistic message. (ANSI allows the #definition of NULL to be (void *)0, which will not work in non-pointer contexts.) In particular, do not use NULL when the ASCII null character (NUL) is desired. Provide your own definition #define NUL '\0' if you must. References: K&R II Sec. 5.4 p. 102. 1.9: But wouldn't it be better to use NULL (rather than 0) in case the value of NULL changes, perhaps on a machine with nonzero null pointers? A: No. Although symbolic constants are often used in place of numbers because the numbers might change, this is _not_ the reason that NULL is used in place of 0. Once again, the language guarantees that source-code 0's (in pointer contexts) generate null pointers. NULL is used only as a stylistic convention. 1.10: I'm confused. NULL is guaranteed to be 0, but the null pointer is not? A: When the term "null" or "NULL" is casually used, one of several things may be meant: 1. The conceptual null pointer, the abstract language concept defined in question 1.1. It is implemented with... 2. The internal (or run-time) representation of a null pointer, which may or may not be all-bits-0 and which may be different for different pointer types. The actual values should be of concern only to compiler writers. Authors of C programs never see them, since they use... 3. The source code syntax for null pointers, which is the single character "0". It is often hidden behind... 4. The NULL macro, which is #defined to be "0" or "(void *)0". Finally, as red herrings, we have... 5. The ASCII null character (NUL), which does have all bits zero, but has no necessary relation to the null pointer except in name; and... 6. The "null string," which is another name for an empty string (""). The term "null string" can be confusing in C (and should perhaps be avoided), because it involves a null ('\0') character, but not a null pointer, which brings us full circle... This article always uses the phrase "null pointer" (in lower case) for sense 1, the character "0" for sense 3, and the capitalized word "NULL" for sense 4. 1.11: Why is there so much confusion surrounding null pointers? Why do these questions come up so often? A: C programmers traditionally like to know more than they need to about the underlying machine implementation. The fact that null pointers are represented both in source code, and internally to most machines, as zero invites unwarranted assumptions. The use of a preprocessor macro (NULL) suggests that the value might change later, or on some weird machine. The construct "if(p == 0)" is easily misread as calling for conversion of p to an integral type, rather than 0 to a pointer type, before the comparison. Finally, the distinction between the several uses of the term "null" (listed above) is often overlooked. One good way to wade out of the confusion is to imagine that C had a keyword (perhaps "nil", like Pascal) with which null pointers were requested. The compiler could either turn "nil" into the correct type of null pointer, when it could determine the type from the source code, or complain when it could not. Now, in fact, in C the keyword for a null pointer is not "nil" but "0", which works almost as well, except that an uncast "0" in a non-pointer context generates an integer zero instead of an error message, and if that uncast 0 was supposed to be a null pointer, the code may not work. 1.12: I'm still confused. I just can't understand all this null pointer stuff. A: Follow these two simple rules: 1. When you want to refer to a null pointer in source code, use "0" or "NULL". 2. If the usage of "0" or "NULL" is an argument in a function call, cast it to the pointer type expected by the function being called. The rest of the discussion has to do with other people's misunderstandings, or with the internal representation of null pointers (which you shouldn't need to know), or with ANSI C refinements. Understand questions 1.1, 1.2, and 1.3, and consider 1.8 and 1.11, and you'll do fine. 1.13: Given all the confusion surrounding null pointers, wouldn't it be easier simply to require them to be represented internally by zeroes? A: If for no other reason, doing so would be ill-advised because it would unnecessarily constrain implementations which would otherwise naturally represent null pointers by special, nonzero bit patterns, particularly when those values would trigger automatic hardware traps for invalid accesses. Besides, what would this requirement really accomplish? Proper understanding of null pointers does not require knowledge of the internal representation, whether zero or nonzero. Assuming that null pointers are internally zero does not make any code easier to write (except for a certain ill-advised usage of calloc; see question 3.13). Known-zero internal pointers would not obviate casts in function calls, because the _size_ of the pointer might still be different from that of an int. (If "nil" were used to request null pointers rather than "0," as mentioned in question 1.11, the urge to assume an internal zero representation would not even arise.) 1.14: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types? A: The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to all the extant poorly-written C code which made incorrect assumptions. Older, word-addressed Prime machines were also notorious for requiring larger byte pointers (char *'s) than word pointers (int *'s). The Eclipse MV series from Data General has three architecturally supported pointer formats (word, byte, and bit pointers), two of which are used by C compilers: byte pointers for char * and void *, and word pointers for everything else. Some Honeywell-Bull mainframes use the bit pattern 06000 for (internal) null pointers. The CDC Cyber 180 Series has 48-bit pointers consisting of a ring, segment, and offset. Most users (in ring 11) have null pointers of 0xB00000000000. The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair (basically a nonexistent handle) as a C null pointer. Depending on the "memory model" in use, 80*86 processors (PC's) may use 16 bit data pointers and 32 bit function pointers, or vice versa. The old HP 3000 series computers use a different addressing scheme for byte addresses than for word addresses; void and char pointers therefore have a different representation than an int (structure, etc.) pointer to the same address would have. 1.15: What does a run-time "null pointer assignment" error mean? How do I track it down? A: This message, which occurs only under MS-DOS (see, therefore, section 16) means that you've written, via an unintialized and/or null pointer, to location zero. A debugger will usually let you set a data breakpoint on location 0. Alternately, you could write a bit of code to copy 20 or so bytes from location 0 into another buffer, and periodically check that it hasn't changed. Section 2. Arrays and Pointers 2.1: I had the definition char a[6] in one source file, and in another I declared extern char *a. Why didn't it work? A: The declaration extern char *a simply does not match the actual definition. The type "pointer-to-type-T" is not the same as "array-of-type-T." Use extern char a[]. References: CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 pp. 64-5. 2.2: But I heard that char a[] was identical to char *a. A: Not at all. (What you heard has to do with formal parameters to functions; see question 2.4.) Arrays are not pointers. The array declaration "char a[6];" requests that space for six characters be set aside, to be known by the name "a." That is, there is a location named "a" at which six characters can sit. The pointer declaration "char *p;" on the other hand, requests a place which holds a pointer. The pointer is to be known by the name "p," and can point to any char (or contiguous array of chars) anywhere. As usual, a picture is worth a thousand words. The statements char a[] = "hello"; char *p = "world"; would result in data structures which could be represented like this: +---+---+---+---+---+---+ a: | h | e | l | l | o |\0 | +---+---+---+---+---+---+ +-----+ +---+---+---+---+---+---+ p: | *======> | w | o | r | l | d |\0 | +-----+ +---+---+---+---+---+---+ It is important to realize that a reference like x[3] generates different code depending on whether x is an array or a pointer. Given the declarations above, when the compiler sees the expression a[3], it emits code to start at the location "a," move three past it, and fetch the character there. When it sees the expression p[3], it emits code to start at the location "p," fetch the pointer value there, add three to the pointer, and finally fetch the character pointed to. In the example above, both a[3] and p[3] happen to be the character 'l', but the compiler gets there differently. (See also questions 17.19 and 17.20.) 2.3: So what is meant by the "equivalence of pointers and arrays" in C? A: Much of the confusion surrounding pointers in C can be traced to a misunderstanding of this statement. Saying that arrays and pointers are "equivalent" neither means that they are identical nor even interchangeable. "Equivalence" refers to the following key definition: An lvalue [see question 2.5] of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T. (The exceptions are when the array is the operand of a sizeof or & operator, or is a literal string initializer for a character array.) As a consequence of this definition, there is no apparent difference in the behavior of the "array subscripting" operator [] as it applies to arrays and pointers. In an expression of the form a[i], the array reference "a" decays into a pointer, following the rule above, and is then subscripted just as would be a pointer variable in the expression p[i] (although the eventual memory accesses will be different, as explained in question 2.2). In either case, the expression x[i] (where x is an array or a pointer) is, by definition, identical to *((x)+(i)). References: K&R I Sec. 5.3 pp. 93-6; K&R II Sec. 5.3 p. 99; H&S Sec. 5.4.1 p. 93; ANSI Sec. 3.2.2.1, Sec. 3.3.2.1, Sec. 3.3.6 . 2.4: Then why are array and pointer declarations interchangeable as function formal parameters? A: Since arrays decay immediately into pointers, an array is never actually passed to a function. As a convenience, any parameter declarations which "look like" arrays, e.g. f(a) char a[]; are treated by the compiler as if they were pointers, since that is what the function will receive if an array is passed: f(a) char *a; This conversion holds only within function formal parameter declarations, nowhere else. If this conversion bothers you, avoid it; many people have concluded that the confusion it causes outweighs the small advantage of having the declaration "look like" the call and/or the uses within the function. References: K&R I Sec. 5.3 p. 95, Sec. A10.1 p. 205; K&R II Sec. 5.3 p. 100, Sec. A8.6.3 p. 218, Sec. A10.1 p. 226; H&S Sec. 5.4.3 p. 96; ANSI Sec. 3.5.4.3, Sec. 3.7.1, CT&P Sec. 3.3 pp. 33-4. 2.5: How can an array be an lvalue, if you can't assign to it? A: The ANSI C Standard defines a "modifiable lvalue," which an array is not. References: ANSI Sec. 3.2.2.1 p. 37. 2.6: Why doesn't sizeof properly report the size of an array which is a parameter to a function? A: The sizeof operator reports the size of the pointer parameter which the function actually receives (see question 2.4). 2.7: Someone explained to me that arrays were really just constant pointers. A: This is a bit of an oversimplification. An array name is "constant" in that it cannot be assigned to, but an array is _not_ a pointer, as the discussion and pictures in question 2.2 should make clear. 2.8: Practically speaking, what is the difference between arrays and pointers? A: Arrays automatically allocate space, but can't be relocated or resized. Pointers must be explicitly assigned to point to allocated space (perhaps using malloc), but can be reassigned (i.e. pointed at different objects) at will, and have many other uses besides serving as the base of blocks of memory. Due to the so-called equivalence of arrays and pointers (see question 2.3), arrays and pointers often seem interchangeable, and in particular a pointer to a block of memory assigned by malloc is frequently treated (and can be referenced using [] exactly) as if it were a true array. (See question 2.14; see also question 17.20.) 2.9: I came across some "joke" code containing the "expression" 5["abcdef"] . How can this be legal C? A: Yes, Virginia, array subscripting is commutative in C. This curious fact follows from the pointer definition of array subscripting, namely that a[e] is identical to *((a)+(e)), for _any_ expression e and primary expression a, as long as one of them is a pointer expression and one is integral. This unsuspected commutativity is often mentioned in C texts as if it were something to be proud of, but it finds no useful application outside of the Obfuscated C Contest (see question 17.13). References: ANSI Rationale Sec. 3.3.2.1 p. 41. 2.10: My compiler complained when I passed a two-dimensional array to a routine expecting a pointer to a pointer. A: The rule by which arrays decay into pointers is not applied recursively. An array of arrays (i.e. a two-dimensional array in C) decays into a pointer to an array, not a pointer to a pointer. Pointers to arrays can be confusing, and must be treated carefully. (The confusion is heightened by the existence of incorrect compilers, including some versions of pcc and pcc-derived lint's, which improperly accept assignments of multi-dimensional arrays to multi-level pointers.) If you are passing a two-dimensional array to a function: int array[NROWS][NCOLUMNS]; f(array); the function's declaration should match: f(int a[][NCOLUMNS]) {...} or f(int (*ap)[NCOLUMNS]) {...} /* ap is a pointer to an array */ In the first declaration, the compiler performs the usual implicit parameter rewriting of "array of array" to "pointer to array;" in the second form the pointer declaration is explicit. Since the called function does not allocate space for the array, it does not need to know the overall size, so the number of "rows," NROWS, can be omitted. The "shape" of the array is still important, so the "column" dimension NCOLUMNS (and, for 3- or more dimensional arrays, the intervening ones) must be included. If a function is already declared as accepting a pointer to a pointer, it is probably incorrect to pass a two-dimensional array directly to it. References: K&R I Sec. 5.10 p. 110; K&R II Sec. 5.9 p. 113. 2.11: How do I write functions which accept 2-dimensional arrays when the "width" is not known at compile time? A: It's not easy. One way is to pass in a pointer to the [0][0] element, along with the two dimensions, and simulate array subscripting "by hand:" f2(aryp, nrows, ncolumns) int *aryp; int nrows, ncolumns; { ... ary[i][j] is really aryp[i * ncolumns + j] ... } This function could be called with the array from question 2.10 as f2(&array[0][0], NROWS, NCOLUMNS); It must be noted, however, that a program which performs multidimensional array subscripting "by hand" in this way is not in strict conformance with the ANSI C Standard; the behavior of accessing (&array[0][0])[x] is not defined for x > NCOLUMNS. gcc allows local arrays to be declared having sizes which are specified by a function's arguments, but this is a nonstandard extension. See also question 2.15. 2.12: How do I declare a pointer to an array? A: Usually, you don't want to. When people speak casually of a pointer to an array, they usually mean a pointer to its first element. Instead of a pointer to an array, consider using a pointer to one of the array's elements. Arrays of type T decay into pointers to type T (see question 2.3), which is convenient; subscripting or incrementing the resultant pointer accesses the individual members of the array. True pointers to arrays, when subscripted or incremented, step over entire arrays, and are generally only useful when operating on arrays of arrays, if at all. (See question 2.10 above.) If you really need to declare a pointer to an entire array, use something like "int (*ap)[N];" where N is the size of the array. (See also question 10.4.) If the size of the array is unknown, N can be omitted, but the resulting type, "pointer to array of unknown size," is useless. 2.13: Since array references decay to pointers, given int array[NROWS][NCOLUMNS]; what's the difference between array and &array? A: Under ANSI/ISO Standard C, &array yields a pointer, of type pointer-to-array-of-T, to the entire array (see also question 2.12). Under pre-ANSI C, the & in &array generally elicited a warning, and was generally ignored. Under all C compilers, an unadorned reference to an array yields a pointer, of type pointer-to-T, to the array's first element. (See also question 2.3.) 2.14: How can I dynamically allocate a multidimensional array? A: It is usually best to allocate an array of pointers, and then initialize each pointer to a dynamically-allocated "row." Here is a two-dimensional example: int **array1 = (int **)malloc(nrows * sizeof(int *)); for(i = 0; i < nrows; i++) array1[i] = (int *)malloc(ncolumns * sizeof(int)); (In "real" code, of course, malloc would be declared correctly, and each return value checked.) You can keep the array's contents contiguous, while making later reallocation of individual rows difficult, with a bit of explicit pointer arithmetic: int **array2 = (int **)malloc(nrows * sizeof(int *)); array2[0] = (int *)malloc(nrows * ncolumns * sizeof(int)); for(i = 1; i < nrows; i++) array2[i] = array2[0] + i * ncolumns; In either case, the elements of the dynamic array can be accessed with normal-looking array subscripts: array[i][j]. If the double indirection implied by the above schemes is for some reason unacceptable, you can simulate a two-dimensional array with a single, dynamically-allocated one-dimensional array: int *array3 = (int *)malloc(nrows * ncolumns * sizeof(int)); However, you must now perform subscript calculations manually, accessing the i,jth element with array3[i * ncolumns + j]. (A macro can hide the explicit calculation, but invoking it then requires parentheses and commas which don't look exactly like multidimensional array subscripts.) Finally, you can use pointers-to-arrays: int (*array4)[NCOLUMNS] = (int (*)[NCOLUMNS])malloc(nrows * sizeof(*array4)); , but the syntax gets horrific and all but one dimension must be known at compile time. With all of these techniques, you may of course need to remember to free the arrays (which may take several steps; see question 3.9) when they are no longer needed, and you cannot necessarily intermix the dynamically-allocated arrays with conventional, statically-allocated ones (see question 2.15 below, and also question 2.10). 2.15: How can I use statically- and dynamically-allocated multidimensional arrays interchangeably when passing them to functions? A: There is no single perfect method. Given the declarations int array[NROWS][NCOLUMNS]; int **array1; int **array2; int *array3; int (*array4)[NCOLUMNS]; as initialized in the code fragments in questions 2.10 and 2.14, and functions declared as f1(int a[][NCOLUMNS], int m, int n); f2(int *aryp, int nrows, int ncolumns); f3(int **pp, int m, int n); (see questions 2.10 and 2.11), the following calls should work as expected: f1(array, NROWS, NCOLUMNS); f1(array4, nrows, NCOLUMNS); f2(&array[0][0], NROWS, NCOLUMNS); f2(*array2, nrows, ncolumns); f2(array3, nrows, ncolumns); f2(*array4, nrows, NCOLUMNS); f3(array1, nrows, ncolumns); f3(array2, nrows, ncolumns); The following two calls would probably work, but involve questionable casts, and work only if the dynamic ncolumns matches the static NCOLUMNS: f1((int (*)[NCOLUMNS])(*array2), nrows, ncolumns); f1((int (*)[NCOLUMNS])array3, nrows, ncolumns); It must again be noted that passing &array[0][0] to f2() is not strictly conforming; see question 2.11. If you can understand why all of the above calls work and are written as they are, and if you understand why the combinations that are not listed would not work, then you have a _very_ good understanding of arrays and pointers (and several other areas) in C. 2.16: Here's a neat trick: if I write int realarray[10]; int *array = &realarray[-1]; I can treat "array" as if it were a 1-based array. A: Although this technique is attractive (and was used in old editions of the book Numerical Recipes in C), it does not conform to the C standards. Pointer arithmetic is defined only as long as the pointer points within the same allocated block of memory, or to the imaginary "terminating" element one past it; otherwise, the behavior is undefined, _even if the pointer is not dereferenced_. The code above could fail if, while subtracting the offset, an illegal address were generated (perhaps because the address tried to "wrap around" past the beginning of some memory segment). References: ANSI Sec. 3.3.6 p. 48, Rationale Sec. 3.2.2.3 p. 38; K&R II Sec. 5.3 p. 100, Sec. 5.4 pp. 102-3, Sec. A7.7 pp. 205-6. 2.17: I passed a pointer to a function which initialized it: ... int *ip; f(ip); ... void f(ip) int *ip; { static int dummy = 5; ip = &dummy; } , but the pointer in the caller was unchanged. A: Did the function try to initialize the pointer itself, or just what it pointed to? Remember that arguments in C are passed by value. The called function altered only the passed copy of the pointer. You'll either want to pass the address of the pointer (the function will end up accepting a pointer-to-a-pointer), or have the function return the pointer. 2.18: I have a char * pointer that happens to point to some ints, and I want to step it over them. Why doesn't ((int *)p)++; work? A: In C, a cast operator does not mean "pretend these bits have a different type, and treat them accordingly;" it is a conversion operator, and by definition it yields an rvalue, which cannot be assigned to, or incremented with ++. (It is an anomaly in pcc- derived compilers, and an extension in gcc, that expressions such as the above are ever accepted.) Say what you mean: use p = (char *)((int *)p + 1); , or simply p += sizeof(int); References: ANSI Sec. 3.3.4, Rationale Sec. 3.3.2.4 p. 43. 2.19: Can I use a void ** pointer to pass a generic pointer to a function by reference? A: Not portably. There is no generic pointer-to-pointer type in C. void * acts as a generic pointer only because conversions are applied automatically when other pointer types are assigned to and from void *'s; these conversions cannot be performed (the correct underlying pointer type is not known) if an attempt is made to indirect upon a void ** value which points at something other than a void *. Section 3. Memory Allocation 3.1: Why doesn't this fragment work? char *answer; printf("Type something:\n"); gets(answer); printf("You typed \"%s\"\n", answer); A: The pointer variable "answer," which is handed to the gets function as the location into which the response should be stored, has not been set to point to any valid storage. That is, we cannot say where the pointer "answer" points. (Since local variables are not initialized, and typically contain garbage, it is not even guaranteed that "answer" starts out as a null pointer. See question 17.1.) The simplest way to correct the question-asking program is to use a local array, instead of a pointer, and let the compiler worry about allocation: #include char answer[100], *p; printf("Type something:\n"); fgets(answer, sizeof(answer), stdin); if((p = strchr(answer, '\n')) != NULL) *p = '\0'; printf("You typed \"%s\"\n", answer); Note that this example also uses fgets() instead of gets() (always a good idea; see question 11.6), allowing the size of the array to be specified, so that the end of the array will not be overwritten if the user types an overly-long line. (Unfortunately for this example, fgets() does not automatically delete the trailing \n, as gets() would.) It would also be possible to use malloc() to allocate the answer buffer. 3.2: I can't get strcat to work. I tried char *s1 = "Hello, "; char *s2 = "world!"; char *s3 = strcat(s1, s2); but I got strange results. A: Again, the problem is that space for the concatenated result is not properly allocated. C does not provide an automatically- managed string type. C compilers only allocate memory for objects explicitly mentioned in the source code (in the case of "strings," this includes character arrays and string literals). The programmer must arrange (explicitly) for sufficient space for the results of run-time operations such as string concatenation, typically by declaring arrays, or by calling malloc. (See also question 17.20.) strcat performs no allocation; the second string is appended to the first one, in place. Therefore, one fix would be to declare the first string as an array with sufficient space: char s1[20] = "Hello, "; Since strcat returns the value of its first argument (s1, in this case), the s3 variable is superfluous. References: CT&P Sec. 3.2 p. 32. 3.3: But the man page for strcat says that it takes two char *'s as arguments. How am I supposed to know to allocate things? A: In general, when using pointers you _always_ have to consider memory allocation, at least to make sure that the compiler is doing it for you. If a library routine's documentation does not explicitly mention allocation, it is usually the caller's problem. The Synopsis section at the top of a Unix-style man page can be misleading. The code fragments presented there are closer to the function definition used by the call's implementor than the invocation used by the caller. In particular, many routines which accept pointers (e.g. to structs or strings), are usually called with the address of some object (a struct, or an array -- see questions 2.3 and 2.4.) Another common example is stat(). 3.4: I have a function that is supposed to return a string, but when it returns to its caller, the returned string is garbage. A: Make sure that the memory to which the function returns a pointer is correctly allocated. The returned pointer should be to a statically-allocated buffer, or to a buffer passed in by the caller, or to memory obtained with malloc(), but _not_ to a local (auto) array. In other words, never do something like char *f() { char buf[10]; /* ... */ return buf; } One fix (which is imperfect, especially if f() is called recursively, or if several of its return values are needed simultaneously) would to to declare the buffer as static char buf[10]; See also question 17.5. 3.5: Why does some code carefully cast the values returned by malloc to the pointer type being allocated? A: Before ANSI/ISO Standard C introduced the void * generic pointer type, these casts were typically required to silence warnings about assignment between incompatible pointer types. (Under ANSI/ISO Standard C, these casts are not required.) 3.6: You can't use dynamically-allocated memory after you free it, can you? A: No. Some early documentation for malloc stated that the contents of freed memory was "left undisturbed;" this ill- advised guarantee was never universal and is not required by ANSI. Few programmers would use the contents of freed memory deliberately, but it is easy to do so accidentally. Consider the following (correct) code for freeing a singly-linked list: struct list *listp, *nextp; for(listp = base; listp != NULL; listp = nextp) { nextp = listp->next; free((char *)listp); } and notice what would happen if the more-obvious loop iteration expression listp = listp->next were used, without the temporary nextp pointer. References: ANSI Rationale Sec. 4.10.3.2 p. 102; CT&P Sec. 7.10 p. 95. 3.7: How does free() know how many bytes to free? A: The malloc/free package remembers the size of each block it allocates and returns, so it is not necessary to remind it of the size when freeing. 3.8: So can I query the malloc package to find out how big an allocated block is? A: Not portably. 3.9: I'm allocating structures which contain pointers to other dynamically-allocated objects. When I free a structure, do I have to free each subsidiary pointer first? A: Yes. In general, you must arrange that each pointer returned from malloc be individually passed to free, exactly once (if it is freed at all). 3.10: I have a program which mallocs but then frees a lot of memory, but memory usage (as reported by ps) doesn't seem to go back down. A: Most implementations of malloc/free do not return freed memory to the operating system (if there is one), but merely make it available for future malloc calls within the same process. 3.11: Must I free allocated memory before the program exits? A: You shouldn't have to. A real operating system definitively reclaims all memory when a program exits. Nevertheless, some personal computers are said not to reliably recover memory, and all that can be inferred from the ANSI/ISO C Standard is that it is a "quality of implementation issue." References: ANSI Sec. 4.10.3.2 . 3.12: Is it legal to pass a null pointer as the first argument to realloc()? Why would you want to? A: ANSI C sanctions this usage (and the related realloc(..., 0), which frees), but several earlier implementations do not support it, so it is not widely portable. Passing an initially-null pointer to realloc can make it easier to write a self-starting incremental allocation algorithm. References: ANSI Sec. 4.10.3.4 . 3.13: What is the difference between calloc and malloc? Is it safe to use calloc's zero-fill guarantee for pointer and floating-point values? Does free work on memory allocated with calloc, or do you need a cfree? A: calloc(m, n) is essentially equivalent to p = malloc(m * n); memset(p, 0, m * n); The zero fill is all-bits-zero, and does not therefore guarantee useful zero values for pointers (see section 1 of this list) or floating-point values. free can (and should) be used to free the memory allocated by calloc. References: ANSI Secs. 4.10.3 to 4.10.3.2 . 3.14: What is alloca and why is its use discouraged? A: alloca allocates memory which is automatically freed when the function which called alloca returns. That is, memory allocated with alloca is local to a particular function's "stack frame" or context. alloca cannot be written portably, and is difficult to implement on machines without a stack. Its use is problematical (and the obvious implementation on a stack-based machine fails) when its return value is passed directly to another function, as in fgets(alloca(100), 100, stdin). For these reasons, alloca cannot be used in programs which must be widely portable, no matter how useful it might be. References: ANSI Rationale Sec. 4.10.3 p. 102. Section 4. Expressions 4.1: Why doesn't this code: a[i] = i++; work? A: The subexpression i++ causes a side effect -- it modifies i's value -- which leads to undefined behavior if i is also referenced elsewhere in the same expression. (Note that although the language in K&R suggests that the behavior of this expression is unspecified, the ANSI/ISO C Standard makes the stronger statement that it is undefined -- see question 5.23.) References: ANSI Sec. 3.3 p. 39. 4.2: Under my compiler, the code int i = 7; printf("%d\n", i++ * i++); prints 49. Regardless of the order of evaluation, shouldn't it print 56? A: Although the postincrement and postdecrement operators ++ and -- perform the operations after yielding the former value, the implication of "after" is often misunderstood. It is _not_ guaranteed that the operation is performed immediately after giving up the previous value and before any other part of the expression is evaluated. It is merely guaranteed that the update will be performed sometime before the expression is considered "finished" (before the next "sequence point," in ANSI C's terminology). In the example, the compiler chose to multiply the previous value by itself and to perform both increments afterwards. The behavior of code which contains multiple, ambiguous side effects has always been undefined (see question 5.23). Don't even try to find out how your compiler implements such things (contrary to the ill-advised exercises in many C textbooks); as K&R wisely point out, "if you don't know _how_ they are done on various machines, that innocence may help to protect you." References: K&R I Sec. 2.12 p. 50; K&R II Sec. 2.12 p. 54; ANSI Sec. 3.3 p. 39; CT&P Sec. 3.7 p. 47; PCS Sec. 9.5 pp. 120-1. (Ignore H&S Sec. 7.12 pp. 190-1, which is obsolete.) 4.3: I've experimented with the code int i = 2; i = i++; on several compilers. Some gave i the value 2, some gave 3, but one gave 4. I know the behavior is undefined, but how could it give 4? A: Undefined behavior means _anything_ can happen. See question 5.23. 4.4: People keep saying the behavior is undefined, but I just tried it on an ANSI-conforming compiler, and got the results I expected. A: A compiler may do anything it likes when faced with undefined behavior (and, within limits, with implementation-defined and unspecified behavior), including doing what you expect. It's unwise to depend on it, though. See also question 5.18. 4.5: Can I use explicit parentheses to force the order of evaluation I want? Even if I don't, doesn't precedence dictate it? A: Operator precedence and explicit parentheses impose only a partial ordering on the evaluation of an expression. Consider the expression f() + g() * h() -- although we know that the multiplication will happen before the addition, there is no telling which of the three functions will be called first. 4.6: But what about the &&, ||, and comma operators? I see code like "if((c = getchar()) == EOF || c == '\n')" ... A: There is a special exception for those operators, (as well as the ?: operator); each of them does imply a sequence point (i.e. left-to-right evaluation is guaranteed). Any book on C should make this clear. References: K&R I Sec. 2.6 p. 38, Secs. A7.11-12 pp. 190-1; K&R II Sec. 2.6 p. 41, Secs. A7.14-15 pp. 207-8; ANSI Secs. 3.3.13 p. 52, 3.3.14 p. 52, 3.3.15 p. 53, 3.3.17 p. 55, CT&P Sec. 3.7 pp. 46-7. 4.7: If I'm not using the value of the expression, should I use i++ or ++i to increment a variable? A: Since the two forms differ only in the value yielded, they are entirely equivalent when only their side effect is needed. 4.8: Why doesn't the code int a = 1000, b = 1000; long int c = a * b; work? A: Under C's integral promotion rules, the multiplication is carried out using int arithmetic, and the result may overflow and/or be truncated before being assigned to the long int left- hand-side. Use an explicit cast to force long arithmetic: long int c = (long int)a * b; Note that the code (long int)(a * b) would _not_ have the desired effect. Section 5. ANSI C 5.1: What is the "ANSI C Standard?" A: In 1983, the American National Standards Institute (ANSI) commissioned a committee, X3J11, to standardize the C language. After a long, arduous process, including several widespread public reviews, the committee's work was finally ratified as ANS X3.159-1989, on December 14, 1989, and published in the spring of 1990. For the most part, ANSI C standardizes existing practice, with a few additions from C++ (most notably function prototypes) and support for multinational character sets (including the much-lambasted trigraph sequences). The ANSI C standard also formalizes the C run-time library support routines. The published Standard includes a "Rationale," which explains many of its decisions, and discusses a number of subtle points, including several of those covered here. (The Rationale is "not part of ANSI Standard X3.159-1989, but is included for information only.") The Standard has been adopted as an international standard, ISO/IEC 9899:1990, although the sections are numbered differently (briefly, ANSI sections 2 through 4 correspond roughly to ISO sections 5 through 7), and the Rationale is currently not included. 5.2: How can I get a copy of the Standard? A: ANSI X3.159 has been officially superseded by ISO 9899. Copies are available in the United States from American National Standards Institute 11 W. 42nd St., 13th floor New York, NY 10036 USA (+1) 212 642 4900 or Global Engineering Documents 2805 McGaw Avenue Irvine, CA 92714 USA (+1) 714 261 1455 (800) 854 7179 (U.S. & Canada) In other countries, contact the appropriate national standards body, or ISO in Geneva at: ISO Sales Case Postale 56 CH-1211 Geneve 20 Switzerland The cost is $130.00 from ANSI or $162.50 from Global. Copies of the original X3.159 (including the Rationale) are still available at $205.00 from ANSI or $200.50 from Global. Note that ANSI derives revenues to support its operations from the sale of printed standards, so electronic copies are _not_ available. The mistitled _Annotated ANSI C Standard_, with annotations by Herbert Schildt, contains the full text of ISO 9899; it is published by Osborne/McGraw-Hill, ISBN 0-07-881952-0, and sells in the U.S. for approximately $40. (It has been suggested that the price differential between this work and the official standard reflects the value of the annotations.) The text of the Rationale (not the full Standard) is now available for anonymous ftp from ftp.uu.net (see question 17.12) in directory doc/standards/ansi/X3.159-1989 . The Rationale has also been printed by Silicon Press, ISBN 0-929306-07-4. 5.3: Does anyone have a tool for converting old-style C programs to ANSI C, or vice versa, or for automatically generating prototypes? A: Two programs, protoize and unprotoize, convert back and forth between prototyped and "old style" function definitions and declarations. (These programs do _not_ handle full-blown translation between "Classic" C and ANSI C.) These programs were once patches to the FSF GNU C compiler, gcc, but are now part of the main gcc distribution; look in pub/gnu at prep.ai.mit.edu (18.71.0.38), or at several other FSF archive sites. The unproto program (/pub/unix/unproto5.shar.Z on ftp.win.tue.nl) is a filter which sits between the preprocessor and the next compiler pass, converting most of ANSI C to traditional C on-the-fly. The GNU GhostScript package comes with a little program called ansi2knr. Several prototype generators exist, many as modifications to lint. Version 3 of CPROTO was posted to comp.sources.misc in March, 1992. There is another program called "cextract." See also question 17.12. Finally, are you sure you really need to convert lots of old code to ANSI C? The old-style function syntax is still acceptable. 5.4: I'm trying to use the ANSI "stringizing" preprocessing operator # to insert the value of a symbolic constant into a message, but it keeps stringizing the macro's name rather than its value. A: You must use something like the following two-step procedure to force the macro to be expanded as well as stringized: #define str(x) #x #define xstr(x) str(x) #define OP plus char *opname = xstr(OP); This sets opname to "plus" rather than "OP". An equivalent circumlocution is necessary with the token-pasting operator ## when the values (rather than the names) of two macros are to be concatenated. References: ANSI Sec. 3.8.3.2, Sec. 3.8.3.5 example p. 93. 5.5: I don't understand why I can't use const values in initializers and array dimensions, as in const int n = 5; int a[n]; A: The const qualifier really means "read-only;" an object so qualified is a normal run-time object which cannot (normally) be assigned to. The value of a const-qualified object is therefore _not_ a constant expression in the full sense of the term. (C is unlike C++ in this regard.) When you need a true compile- time constant, use a preprocessor #define. References: ANSI Sec. 3.4 . 5.6: What's the difference between "char const *p" and "char * const p"? A: "char const *p" is a pointer to a constant character (you can't change the character); "char * const p" is a constant pointer to a (variable) character (i.e. you can't change the pointer). (Read these "inside out" to understand them. See question 10.4.) References: ANSI Sec. 3.5.4.1 . 5.7: Why can't I pass a char ** to a function which expects a const char **? A: You can use a pointer-to-T (for any type T) where a pointer-to- const-T is expected, but the rule (an explicit exception) which permits slight mismatches in qualified pointer types is not applied recursively, but only at the top level. You must use explicit casts (e.g. (const char **) in this case) when assigning (or passing) pointers which have qualifier mismatches at other than the first level of indirection. References: ANSI Sec. 3.1.2.6 p. 26, Sec. 3.3.16.1 p. 54, Sec. 3.5.3 p. 65. 5.8: My ANSI compiler complains about a mismatch when it sees extern int func(float); int func(x) float x; {... A: You have mixed the new-style prototype declaration "extern int func(float);" with the old-style definition "int func(x) float x;". It is usually safe to mix the two styles (see question 5.9), but not in this case. Old C (and ANSI C, in the absence of prototypes, and in variable-length argument lists) "widens" certain arguments when they are passed to functions. floats are promoted to double, and characters and short integers are promoted to ints. (For old-style function definitions, the values are automatically converted back to the corresponding narrower types within the body of the called function, if they are declared that way there.) This problem can be fixed either by using new-style syntax consistently in the definition: int func(float x) { ... } or by changing the new-style prototype declaration to match the old-style definition: extern int func(double); (In this case, it would be clearest to change the old-style definition to use double as well, as long as the address of that parameter is not taken.) It may also be safer to avoid "narrow" (char, short int, and float) function arguments and return types. References: ANSI Sec. 3.3.2.2 . 5.9: Can you mix old-style and new-style function syntax? A: Doing so is perfectly legal, as long as you're careful (see especially question 5.8). Note however that old-style syntax is marked as obsolescent, and support for it may be removed some day. References: ANSI Secs. 3.7.1, 3.9.5 . 5.10: Why does the declaration extern f(struct x {int s;} *p); give me an obscure warning message about "struct x introduced in prototype scope"? A: In a quirk of C's normal block scoping rules, a struct declared only within a prototype cannot be compatible with other structs declared in the same source file, nor can the struct tag be used later as you'd expect (it goes out of scope at the end of the prototype). To resolve the problem, precede the prototype with the vacuous- looking declaration struct x; , which will reserve a place at file scope for struct x's definition, which will be completed by the struct declaration within the prototype. References: ANSI Sec. 3.1.2.1 p. 21, Sec. 3.1.2.6 p. 26, Sec. 3.5.2.3 p. 63. 5.11: I'm getting strange syntax errors inside code which I've #ifdeffed out. A: Under ANSI C, the text inside a "turned off" #if, #ifdef, or #ifndef must still consist of "valid preprocessing tokens." This means that there must be no unterminated comments or quotes (note particularly that an apostrophe within a contracted word could look like the beginning of a character constant), and no newlines inside quotes. Therefore, natural-language comments and pseudocode should always be written between the "official" comment delimiters /* and */. (But see also question 17.14, and 6.7.) References: ANSI Sec. 2.1.1.2 p. 6, Sec. 3.1 p. 19 line 37. 5.12: Can I declare main as void, to shut off these annoying "main returns no value" messages? (I'm calling exit(), so main doesn't return.) A: No. main must be declared as returning an int, and as taking either zero or two arguments (of the appropriate type). If you're calling exit() but still getting warnings, you'll have to insert a redundant return statement (or use some kind of "notreached" directive, if available). Declaring a function as void does not merely silence warnings; it may also result in a different function call/return sequence, incompatible with what the caller (in main's case, the C run- time startup code) expects. References: ANSI Sec. 2.1.2.2.1 pp. 7-8. 5.13: Is exit(status) truly equivalent to returning status from main? A: Formally, yes, although discrepancies arise under a few older, nonconforming systems, or if data local to main() might be needed during cleanup (due perhaps to a setbuf or atexit call), or if main() is called recursively. References: ANSI Sec. 2.1.2.2.3 p. 8. 5.14: Why does the ANSI Standard not guarantee more than six monocase characters of external identifier significance? A: The problem is older linkers which are neither under the control of the ANSI standard nor the C compiler developers on the systems which have them. The limitation is only that identifiers be _significant_ in the first six characters, not that they be restricted to six characters in length. This limitation is annoying, but certainly not unbearable, and is marked in the Standard as "obsolescent," i.e. a future revision will likely relax it. This concession to current, restrictive linkers really had to be made, no matter how vehemently some people oppose it. (The Rationale notes that its retention was "most painful.") If you disagree, or have thought of a trick by which a compiler burdened with a restrictive linker could present the C programmer with the appearance of more significance in external identifiers, read the excellently-worded section 3.1.2 in the X3.159 Rationale (see question 5.1), which discusses several such schemes and explains why they could not be mandated. References: ANSI Sec. 3.1.2 p. 21, Sec. 3.9.1 p. 96, Rationale Sec. 3.1.2 pp. 19-21. 5.15: What is the difference between memcpy and memmove? A: memmove offers guaranteed behavior if the source and destination arguments overlap. memcpy makes no such guarantee, and may therefore be more efficiently implementable. When in doubt, it's safer to use memmove. References: ANSI Secs. 4.11.2.1, 4.11.2.2, Rationale Sec. 4.11.2 . 5.16: My compiler is rejecting the simplest possible test programs, with all kinds of syntax errors. A: Perhaps it is a pre-ANSI compiler, unable to accept function prototypes and the like. See also questions 5.17 and 17.2. 5.17: Why are some ANSI/ISO Standard library routines showing up as undefined, even though I've got an ANSI compiler? A: It's not unusual to have a compiler available which accepts ANSI syntax, but not to have ANSI-compatible header files or run-time libraries installed. See also questions 5.16 and 17.2. 5.18: Why won't the Frobozz Magic C Compiler, which claims to be ANSI compliant, accept this code? I know that the code is ANSI, because gcc accepts it. A: Most compilers support a few non-Standard extensions, gcc more so than most. Are you sure that the code being rejected doesn't rely on such an extension? It is usually a bad idea to perform experiments with a particular compiler to determine properties of a language; the applicable standard may permit variations, or the compiler may be wrong. See also question 4.4. 5.19: Why can't I perform arithmetic on a void * pointer? A: The compiler doesn't know the size of the pointed-to objects. Before performing arithmetic, cast the pointer either to char * or to the type you're trying to manipulate (but see question 2.18). 5.20: Is char a[3] = "abc"; legal? What does it mean? A: It is legal in ANSI C (and perhaps in a few pre-ANSI systems), though questionably useful. It declares an array of size three, initialized with the three characters 'a', 'b', and 'c', without the usual terminating '\0' character; the array is therefore not a true C string and cannot be used with strcpy, printf %s, etc. References: ANSI Sec. 3.5.7 pp. 72-3. 5.21: What are #pragmas and what are they good for? A: The #pragma directive provides a single, well-defined "escape hatch" which can be used for all sorts of implementation- specific controls and extensions: source listing control, structure packing, warning suppression (like the old lint /* NOTREACHED */ comments), etc. References: ANSI Sec. 3.8.6 . 5.22: What does "#pragma once" mean? I found it in some header files. A: It is an extension implemented by some preprocessors to help make header files idempotent; it is essentially equivalent to the #ifndef trick mentioned in question 6.4. 5.23: People seem to make a point of distinguishing between implementation-defined, unspecified, and undefined behavior. What's the difference? A: Briefly: implementation-defined means that an implementation must choose some behavior and document it. Unspecified means that an implementation should choose some behavior, but need not document it. Undefined means that absolutely anything might happen. In no case does the Standard impose requirements; in the first two cases it occasionally suggests (and may require a choice from among) a small set of likely behaviors. If you're interested in writing portable code, you can ignore the distinctions, as you'll want to avoid code that depends on any of the three behaviors. References: ANSI Sec. 1.6, especially the Rationale. Section 6. C Preprocessor 6.1: How can I write a generic macro to swap two values? A: There is no good answer to this question. If the values are integers, a well-known trick using exclusive-OR could perhaps be used, but it will not work for floating-point values or pointers, or if the two values are the same variable (and the "obvious" supercompressed implementation for integral types a^=b^=a^=b is in fact illegal due to multiple side-effects; see questions 4.1 and 4.2). If the macro is intended to be used on values of arbitrary type (the usual goal), it cannot use a temporary, since it does not know what type of temporary it needs, and standard C does not provide a typeof operator. The best all-around solution is probably to forget about using a macro, unless you're willing to pass in the type as a third argument. 6.2: I have some old code that tries to construct identifiers with a macro like #define Paste(a, b) a/**/b but it doesn't work any more. A: That comments disappeared entirely and could therefore be used for token pasting was an undocumented feature of some early preprocessor implementations, notably Reiser's. ANSI affirms (as did K&R) that comments are replaced with white space. However, since the need for pasting tokens was demonstrated and real, ANSI introduced a well-defined token-pasting operator, ##, which can be used like this: #define Paste(a, b) a##b (See also question 5.4.) References: ANSI Sec. 3.8.3.3 p. 91, Rationale pp. 66-7. 6.3: What's the best way to write a multi-statement cpp macro? A: The usual goal is to write a macro that can be invoked as if it were a single function-call statement. This means that the "caller" will be supplying the final semicolon, so the macro body should not. The macro body cannot be a simple brace- delineated compound statement, because syntax errors would result if it were invoked (apparently as a single statement, but with a resultant extra semicolon) as the if branch of an if/else statement with an explicit else clause. The traditional solution is to use #define Func() do { \ /* declarations */ \ stmt1; \ stmt2; \ /* ... */ \ } while(0) /* (no trailing ; ) */ When the "caller" appends a semicolon, this expansion becomes a single statement regardless of context. (An optimizing compiler will remove any "dead" tests or branches on the constant condition 0, although lint may complain.) If all of the statements in the intended macro are simple expressions, with no declarations or loops, another technique is to write a single, parenthesized expression using one or more comma operators. (See the example under question 6.10 below. This technique also allows a value to be "returned.") References: CT&P Sec. 6.3 pp. 82-3. 6.4: Is it acceptable for one header file to #include another? A: It's a question of style, and thus receives considerable debate. Many people believe that "nested #include files" are to be avoided: the prestigious Indian Hill Style Guide (see question 14.3) disparages them; they can make it harder to find relevant definitions; they can lead to multiple-declaration errors if a file is #included twice; and they make manual Makefile maintenance very difficult. On the other hand, they make it possible to use header files in a modular way (a header file #includes what it needs itself, rather than requiring each #includer to do so, a requirement that can lead to intractable headaches); a tool like grep (or a tags file) makes it easy to find definitions no matter where they are; a popular trick: #ifndef HEADERUSED #define HEADERUSED ...header file contents... #endif makes a header file "idempotent" so that it can safely be #included multiple times; and automated Makefile maintenance tools (which are a virtual necessity in large projects anyway) handle dependency generation in the face of nested #include files easily. See also section 14. 6.5: Does the sizeof operator work in preprocessor #if directives? A: No. Preprocessing happens during an earlier pass of compilation, before type names have been parsed. Consider using the predefined constants in ANSI's , if applicable, or a "configure" script, instead. (Better yet, try to write code which is inherently insensitive to type sizes.) References: ANSI Sec. 2.1.1.2 pp. 6-7, Sec. 3.8.1 p. 87 footnote 83. 6.6: How can I use a preprocessor #if expression to tell if a machine is big-endian or little-endian? A: You probably can't. (Preprocessor arithmetic uses only long ints, and there is no concept of addressing.) Are you sure you need to know the machine's endianness explicitly? Usually it's better to write code which doesn't care. 6.7: I've got this tricky processing I want to do at compile time and I can't figure out a way to get cpp to do it. A: cpp is not intended as a general-purpose preprocessor. Rather than forcing it to do something inappropriate, consider writing your own little special-purpose preprocessing tool, instead. You can easily get a utility like make(1) to run it for you automatically. If you are trying to preprocess something other than C, consider using a general-purpose preprocessor (such as m4). 6.8: I inherited some code which contains far too many #ifdef's for my taste. How can I preprocess the code to leave only one conditional compilation set, without running it through cpp and expanding all of the #include's and #define's as well? A: There are programs floating around called unifdef, rmifdef, and scpp which do exactly this. (See question 17.12.) 6.9: How can I list all of the pre#defined identifiers? A: There's no standard way, although it is a frequent need. If the compiler documentation is unhelpful, the most expedient way is probably to extract printable strings from the compiler or preprocessor executable with something like the Unix strings(1) utility. Beware that many traditional system-selective pre#defined identifiers (e.g. "unix") are non-Standard (because they clash with the user's namespace) and are being removed or renamed. 6.10: How can I write a cpp macro which takes a variable number of arguments? A: One popular trick is to define the macro with a single argument, and call it with a double set of parentheses, which appear to the preprocessor to indicate a single argument: #define DEBUG(args) (printf("DEBUG: "), printf args) if(n != 0) DEBUG(("n is %d\n", n)); The obvious disadvantage is that the caller must always remember to use the extra parentheses. Other solutions are to use different macros (DEBUG1, DEBUG2, etc.) depending on the number of arguments, or to play games with commas: #define DEBUG(args) (printf("DEBUG: "), printf(args)) #define _ , DEBUG("i = %d" _ i) It is often better to use a bona-fide function, which can take a variable number of arguments in a well-defined way. See questions 7.1 and 7.2. Section 7. Variable-Length Argument Lists 7.1: How can I write a function that takes a variable number of arguments? A: Use the header (or, if you must, the older ). Here is a function which concatenates an arbitrary number of strings into malloc'ed memory: #include /* for malloc, NULL, size_t */ #include /* for va_ stuff */ #include /* for strcat et al */ char *vstrcat(char *first, ...) { size_t len = 0; char *retbuf; va_list argp; char *p; if(first == NULL) return NULL; len = strlen(first); va_start(argp, first); while((p = va_arg(argp, char *)) != NULL) len += strlen(p); va_end(argp); retbuf = malloc(len + 1); /* +1 for trailing \0 */ if(retbuf == NULL) return NULL; /* error */ (void)strcpy(retbuf, first); va_start(argp, first); while((p = va_arg(argp, char *)) != NULL) (void)strcat(retbuf, p); va_end(argp); return retbuf; } Usage is something like char *str = vstrcat("Hello, ", "world!", (char *)NULL); Note the cast on the last argument. (Also note that the caller must free the returned, malloc'ed storage.) Under a pre-ANSI compiler, rewrite the function definition without a prototype ("char *vstrcat(first) char *first; {"), include rather than , add "extern char *malloc();", and use int instead of size_t. You may also have to delete the (void) casts, and use the older varargs package instead of stdarg. See the next question for hints. Remember that in variable-length argument lists, function prototypes do not supply parameter type information; therefore, default argument promotions apply (see question 5.8), and null pointer arguments must be typed explicitly (see question 1.2). References: K&R II Sec. 7.3 p. 155, Sec. B7 p. 254; H&S Sec. 13.4 pp. 286-9; ANSI Secs. 4.8 through 4.8.1.3 . 7.2: How can I write a function that takes a format string and a variable number of arguments, like printf, and passes them to printf to do most of the work? A: Use vprintf, vfprintf, or vsprintf. Here is an "error" routine which prints an error message, preceded by the string "error: " and terminated with a newline: #include #include void error(char *fmt, ...) { va_list argp; fprintf(stderr, "error: "); va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); fprintf(stderr, "\n"); } To use the older package, instead of , change the function header to: void error(va_alist) va_dcl { char *fmt; change the va_start line to va_start(argp); and add the line fmt = va_arg(argp, char *); between the calls to va_start and vfprintf. (Note that there is no semicolon after va_dcl.) References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&S Sec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 . 7.3: How can I discover how many arguments a function was actually called with? A: This information is not available to a portable program. Some old systems provided a nonstandard nargs() function, but its use was always questionable, since it typically returned the number of words passed, not the number of arguments. (Structures and floating point values are usually passed as several words.) Any function which takes a variable number of arguments must be able to determine from the arguments themselves how many of them there are. printf-like functions do this by looking for formatting specifiers (%d and the like) in the format string (which is why these functions fail badly if the format string does not match the argument list). Another common technique (useful when the arguments are all of the same type) is to use a sentinel value (often 0, -1, or an appropriately-cast null pointer) at the end of the list (see the execl and vstrcat examples under questions 1.2 and 7.1 above). 7.4: I can't get the va_arg macro to pull in an argument of type pointer-to-function. A: The type-rewriting games which the va_arg macro typically plays are stymied by overly-complicated types such as pointer-to- function. If you use a typedef for the function pointer type, however, all will be well. References: ANSI Sec. 4.8.1.2 p. 124. 7.5: How can I write a function which takes a variable number of arguments and passes them to some other function (which takes a variable number of arguments)? A: In general, you cannot. You must provide a version of that other function which accepts a va_list pointer, as does vfprintf in the example above. If the arguments must be passed directly as actual arguments (not indirectly through a va_list pointer) to another function which is itself variadic (for which you do not have the option of creating an alternate, va_list-accepting version) no portable solution is possible. (The problem can be solved by resorting to machine-specific assembly language.) 7.6: How can I call a function with an argument list built up at run time? A: There is no guaranteed or portable way to do this. If you're curious, ask this list's editor, who has a few wacky ideas you could try... (See also question 16.11.) Section 8. Boolean Expressions and Variables 8.1: What is the right type to use for boolean values in C? Why isn't it a standard type? Should #defines or enums be used for the true and false values? A: C does not provide a standard boolean type, because picking one involves a space/time tradeoff which is best decided by the programmer. (Using an int for a boolean may be faster, while using char may save data space.) The choice between #defines and enums is arbitrary and not terribly interesting (see also question 9.1). Use any of #define TRUE 1 #define YES 1 #define FALSE 0 #define NO 0 enum bool {false, true}; enum bool {no, yes}; or use raw 1 and 0, as long as you are consistent within one program or project. (An enum may be preferable if your debugger expands enum values when examining variables.) Some people prefer variants like #define TRUE (1==1) #define FALSE (!TRUE) or define "helper" macros such as #define Istrue(e) ((e) != 0) These don't buy anything (see question 8.2 below; see also question 1.6). 8.2: Isn't #defining TRUE to be 1 dangerous, since any nonzero value is considered "true" in C? What if a built-in boolean or relational operator "returns" something other than 1? A: It is true (sic) that any nonzero value is considered true in C, but this applies only "on input", i.e. where a boolean value is expected. When a boolean value is generated by a built-in operator, it is guaranteed to be 1 or 0. Therefore, the test if((a == b) == TRUE) will work as expected (as long as TRUE is 1), but it is obviously silly. In general, explicit tests against TRUE and FALSE are undesirable, because some library functions (notably isupper, isalpha, etc.) return, on success, a nonzero value which is _not_ necessarily 1. (Besides, if you believe that "if((a == b) == TRUE)" is an improvement over "if(a == b)", why stop there? Why not use "if(((a == b) == TRUE) == TRUE)"?) A good rule of thumb is to use TRUE and FALSE (or the like) only for assignment to a Boolean variable or function parameter, or as the return value from a Boolean function, but never in a comparison. The preprocessor macros TRUE and FALSE are used for code readability, not because the underlying values might ever change. (See also questions 1.7 and 1.9.) References: K&R I Sec. 2.7 p. 41; K&R II Sec. 2.6 p. 42, Sec. A7.4.7 p. 204, Sec. A7.9 p. 206; ANSI Secs. 3.3.3.3, 3.3.8, 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, 3.6.5; Achilles and the Tortoise. Section 9. Structs, Enums, and Unions 9.1: What is the difference between an enum and a series of preprocessor #defines? A: At the present time, there is little difference. Although many people might have wished otherwise, the ANSI standard says that enumerations may be freely intermixed with integral types, without errors. (If such intermixing were disallowed without explicit casts, judicious use of enums could catch certain programming errors.) Some advantages of enums are that the numeric values are automatically assigned, that a debugger may be able to display the symbolic values when enum variables are examined, and that they obey block scope. (A compiler may also generate nonfatal warnings when enums and ints are indiscriminately mixed, since doing so can still be considered bad style even though it is not strictly illegal). A disadvantage is that the programmer has little control over the size (or over those nonfatal warnings). References: K&R II Sec. 2.3 p. 39, Sec. A4.2 p. 196; H&S Sec. 5.5 p. 100; ANSI Secs. 3.1.2.5, 3.5.2, 3.5.2.2 . 9.2: I heard that structures could be assigned to variables and passed to and from functions, but K&R I says not. A: What K&R I said was that the restrictions on struct operations would be lifted in a forthcoming version of the compiler, and in fact struct assignment and passing were fully functional in Ritchie's compiler even as K&R I was being published. Although a few early C compilers lacked struct assignment, all modern compilers support it, and it is part of the ANSI C standard, so there should be no reluctance to use it. References: K&R I Sec. 6.2 p. 121; K&R II Sec. 6.2 p. 129; H&S Sec. 5.6.2 p. 103; ANSI Secs. 3.1.2.5, 3.2.2.1, 3.3.16 . 9.3: How does struct passing and returning work? A: When structures are passed as arguments to functions, the entire struct is typically pushed on the stack, using as many words as are required. (Programmers often choose to use pointers to structures instead, precisely to avoid this overhead.) Structures are often returned from functions in a location pointed to by an extra, compiler-supplied "hidden" argument to the function. Some older compilers used a special, static location for structure returns, although this made struct-valued functions nonreentrant, which ANSI C disallows. References: ANSI Sec. 2.2.3 p. 13. 9.4: The following program works correctly, but it dumps core after it finishes. Why? struct list { char *item; struct list *next; } /* Here is the main program. */ main(argc, argv) ... A: A missing semicolon causes the compiler to believe that main returns a structure. (The connection is hard to see because of the intervening comment.) Since struct-valued functions are usually implemented by adding a hidden return pointer, the generated code for main() tries to accept three arguments, although only two are passed (in this case, by the C start-up code). See also question 17.21. References: CT&P Sec. 2.3 pp. 21-2. 9.5: Why can't you compare structs? A: There is no reasonable way for a compiler to implement struct comparison which is consistent with C's low-level flavor. A byte-by-byte comparison could be invalidated by random bits present in unused "holes" in the structure (such padding is used to keep the alignment of later fields correct; see questions 9.10 and 9.11). A field-by-field comparison would require unacceptable amounts of repetitive, in-line code for large structures. If you want to compare two structures, you must write your own function to do so. C++ would let you arrange for the == operator to map to your function. References: K&R II Sec. 6.2 p. 129; H&S Sec. 5.6.2 p. 103; ANSI Rationale Sec. 3.3.9 p. 47. 9.6: How can I read/write structs from/to data files? A: It is relatively straightforward to write a struct out using fwrite: fwrite((char *)&somestruct, sizeof(somestruct), 1, fp); and a corresponding fread invocation can read it back in. However, data files so written will _not_ be very portable (see questions 9.11 and 17.3). Note also that on many systems you must use the "b" flag when fopening the files. 9.7: I came across some code that declared a structure like this: struct name { int namelen; char name[1]; }; and then did some tricky allocation to make the name array act like it had several elements. Is this legal and/or portable? A: This technique is popular, although Dennis Ritchie has called it "unwarranted chumminess with the C implementation." An ANSI Interpretation Ruling has deemed it (more precisely, access beyond the declared size of the name field) to be not strictly conforming, although a thorough treatment of the arguments surrounding the legality of the technique is beyond the scope of this list. It seems, however, to be portable to all known implementations. (Compilers which check array bounds carefully might issue warnings.) To be on the safe side, it may be preferable to declare the variable-size element very large, rather than very small; in the case of the above example: ... char name[MAXSIZE]; ... where MAXSIZE is larger than any name which will be stored. (The trick so modified is said to be in conformance with the Standard.) References: ANSI Rationale Sec. 3.5.4.2 pp. 54-5. 9.8: How can I determine the byte offset of a field within a structure? A: ANSI C defines the offsetof macro, which should be used if available; see . If you don't have it, a suggested implementation is #define offsetof(type, mem) ((size_t) \ ((char *)&((type *) 0)->mem - (char *)((type *) 0))) This implementation is not 100% portable; some compilers may legitimately refuse to accept it. See the next question for a usage hint. References: ANSI Sec. 4.1.5, Rationale Sec. 3.5.4.2 p. 55. 9.9: How can I access structure fields by name at run time? A: Build a table of names and offsets, using the offsetof() macro. The offset of field b in struct a is offsetb = offsetof(struct a, b) If structp is a pointer to an instance of this structure, and b is an int field with offset as computed above, b's value can be set indirectly with *(int *)((char *)structp + offsetb) = value; 9.10: Why does sizeof report a larger size than I expect for a structure type, as if there was padding at the end? A: Structures may have this padding (as well as internal padding; see also question 9.5), so that alignment properties will be preserved when an array of contiguous structures is allocated. 9.11: My compiler is leaving holes in structures, which is wasting space and preventing "binary" I/O to external data files. Can I turn off the padding, or otherwise control the alignment of structs? A: Your compiler may provide an extension to give you this control (perhaps a #pragma), but there is no standard method. See also question 17.3. 9.12: Can I initialize unions? A: ANSI Standard C allows an initializer for the first member of a union. There is no standard way of initializing the other members (nor, under a pre-ANSI compiler, is there generally any way of initializing any of them). 9.13: How can I pass constant values to routines which accept struct arguments? A: C has no way of generating anonymous struct values. You will have to use a temporary struct variable. Section 10. Declarations 10.1: How do you decide which integer type to use? A: If you might need large values (above 32767 or below -32767), use long. Otherwise, if space is very important (there are large arrays or many structures), use short. Otherwise, use int. If well-defined overflow characteristics are important and/or negative values are not, use the corresponding unsigned types. (But beware of mixing signed and unsigned in expressions.) Similar arguments apply when deciding between float and double. Although char or unsigned char can be used as a "tiny" int type, doing so is often more trouble than it's worth, due to unpredictable sign extension and increased code size. These rules obviously don't apply if the address of a variable is taken and must have a particular type. If for some reason you need to declare something with an _exact_ size (usually the only good reason for doing so is when attempting to conform to some externally-imposed storage layout, but see question 17.3), be sure to encapsulate the choice behind an appropriate typedef. 10.2: What should the 64-bit type on new, 64-bit machines be? A: Some vendors of C products for 64-bit machines support 64-bit long ints. Others fear that too much existing code depends on sizeof(int) == sizeof(long) == 32 bits, and introduce a new 64- bit long long (or __longlong) type instead. Programmers interested in writing portable code should therefore insulate their 64-bit type needs behind appropriate typedefs. Vendors who feel compelled to introduce a new, longer integral type should advertise it as being "at least 64 bits" (which is truly new; a type traditional C doesn't have), and not "exactly 64 bits." 10.3: I can't seem to define a linked list successfully. I tried typedef struct { char *item; NODEPTR next; } *NODEPTR; but the compiler gave me error messages. Can't a struct in C contain a pointer to itself? A: Structs in C can certainly contain pointers to themselves; the discussion and example in section 6.5 of K&R make this clear. The problem with this example is that the NODEPTR typedef is not complete at the point where the "next" field is declared. To fix it, first give the structure a tag ("struct node"). Then, declare the "next" field as "struct node *next;", and/or move the typedef declaration wholly before or wholly after the struct declaration. One corrected version would be struct node { char *item; struct node *next; }; typedef struct node *NODEPTR; , and there are at least three other equivalently correct ways of arranging it. A similar problem, with a similar solution, can arise when attempting to declare a pair of typedef'ed mutually referential structures. References: K&R I Sec. 6.5 p. 101; K&R II Sec. 6.5 p. 139; H&S Sec. 5.6.1 p. 102; ANSI Sec. 3.5.2.3 . 10.4: How do I declare an array of N pointers to functions returning pointers to functions returning pointers to characters? A: This question can be answered in at least three ways: 1. char *(*(*a[N])())(); 2. Build the declaration up in stages, using typedefs: typedef char *pc; /* pointer to char */ typedef pc fpc(); /* function returning pointer to char */ typedef fpc *pfpc; /* pointer to above */ typedef pfpc fpfpc(); /* function returning... */ typedef fpfpc *pfpfpc; /* pointer to... */ pfpfpc a[N]; /* array of... */ 3. Use the cdecl program, which turns English into C and vice versa: cdecl> declare a as array of pointer to function returning pointer to function returning pointer to char char *(*(*a[])())() cdecl can also explain complicated declarations, help with casts, and indicate which set of parentheses the arguments go in (for complicated function definitions, like the above). Versions of cdecl are in volume 14 of comp.sources.unix (see question 17.12) and K&R II. Any good book on C should explain how to read these complicated C declarations "inside out" to understand them ("declaration mimics use"). References: K&R II Sec. 5.12 p. 122; H&S Sec. 5.10.1 p. 116. 10.5: I'm building a state machine with a bunch of functions, one for each state. I want to implement state transitions by having each function return a pointer to the next state function. I find a limitation in C's declaration mechanism: there's no way to declare these functions as returning a pointer to a function returning a pointer to a function returning a pointer to a function... A: You can't do it directly. Either have the function return a generic function pointer type, and apply a cast before calling through it; or have it return a structure containing only a pointer to a function returning that structure. 10.6: My compiler is complaining about an invalid redeclaration of a function, but I only define it once and call it once. A: Functions which are called without a declaration in scope (or before they are declared) are assumed to be declared as returning int, leading to discrepancies if the function is later declared otherwise. Non-int functions must be declared before they are called. References: K&R I Sec. 4.2 pp. 70; K&R II Sec. 4.2 p. 72; ANSI Sec. 3.3.2.2 . 10.7: What's the best way to declare and define global variables? A: First, though there can be many _declarations_ (and in many translation units) of a single "global" (strictly speaking, "external") variable (or function), there must be exactly one _definition_. (The definition is the declaration that actually allocates space, and provides an initialization value, if any.) It is best to place the definition in some central (to the program, or to the module) .c file, with an external declaration in a header (".h") file, which is #included wherever the declaration is needed. The .c file containing the definition should also #include the header file containing the external declaration, so that the compiler can check that the declarations match. This rule promotes a high degree of portability, and is consistent with the requirements of the ANSI C Standard. Note that Unix compilers and linkers typically use a "common model" which allows multiple (uninitialized) definitions. A few very odd systems may require an explicit initializer to distinguish a definition from an external declaration. It is possible to use preprocessor tricks to arrange that the declaration need only be typed once, in the header file, and "turned into" a definition, during exactly one #inclusion, via a special #define. References: K&R I Sec. 4.5 pp. 76-7; K&R II Sec. 4.4 pp. 80-1; ANSI Sec. 3.1.2.2 (esp. Rationale), Secs. 3.7, 3.7.2, Sec. F.5.11; H&S Sec. 4.8 pp. 79-80; CT&P Sec. 4.2 pp. 54-56. 10.8: What does extern mean in a function declaration? A: It can be used as a stylistic hint to indicate that the function's definition is probably in another source file, but there is no formal difference between extern int f(); and int f(); References: ANSI Sec. 3.1.2.2 . 10.9: I finally figured out the syntax for declaring pointers to functions, but now how do I initialize one? A: Use something like extern int func(); int (*fp)() = func; When the name of a function appears in an expression but is not being called (i.e. is not followed by a "("), it "decays" into a pointer (i.e. it has its address implicitly taken), much as an array name does. An explicit extern declaration for the function is normally needed, since implicit external function declaration does not happen in this case (again, because the function name is not followed by a "("). 10.10: I've seen different methods used for calling through pointers to functions. What's the story? A: Originally, a pointer to a function had to be "turned into" a "real" function, with the * operator (and an extra pair of parentheses, to keep the precedence straight), before calling: int r, func(), (*fp)() = func; r = (*fp)(); It can also be argued that functions are always called through pointers, but that "real" functions decay implicitly into pointers (in expressions, as they do in initializations) and so cause no trouble. This reasoning, made widespread through pcc and adopted in the ANSI standard, means that r = fp(); is legal and works correctly, whether fp is a function or a pointer to one. (The usage has always been unambiguous; there is nothing you ever could have done with a function pointer followed by an argument list except call through it.) An explicit * is harmless, and still allowed (and recommended, if portability to older compilers is important). References: ANSI Sec. 3.3.2.2 p. 41, Rationale p. 41. 10.11: What's the auto keyword good for? A: Nothing; it's obsolete. Section 11. Stdio 11.1: What's wrong with this code: char c; while((c = getchar()) != EOF)... A: For one thing, the variable to hold getchar's return value must be an int. getchar can return all possible character values, as well as EOF. By passing getchar's return value through a char, either a normal character might be misinterpreted as EOF, or the EOF might be altered (particularly if type char is unsigned) and so never seen. References: CT&P Sec. 5.1 p. 70. 11.2: How can I print a '%' character in a printf format string? I tried \%, but it didn't work. A: Simply double the percent sign: %% . References: K&R I Sec. 7.3 p. 147; K&R II Sec. 7.2 p. 154; ANSI Sec. 4.9.6.1 . 11.3: Why doesn't the code scanf("%d", i); work? A: scanf needs pointers to the variables it is to fill in; you must call scanf("%d", &i); 11.4: Why doesn't this code: double d; scanf("%f", &d); work? A: scanf uses %lf for values of type double, and %f for float. (Note the discrepancy with printf, which uses %f for both double and float, due to C's default argument promotion rules.) 11.5: Why won't the code while(!feof(infp)) { fgets(buf, MAXLINE, infp); fputs(buf, outfp); } work? A: C's I/O is not like Pascal's. EOF is only indicated _after_ an input routine has tried to read, and has reached end-of-file. Usually, you should just check the return value of the input routine (fgets in this case); often, you don't need to use feof() at all. 11.6: Why does everyone say not to use gets()? A: It cannot be told the size of the buffer it's to read into, so it cannot be prevented from overflowing that buffer. See question 3.1 for a code fragment illustrating the replacement of gets() with fgets(). 11.7: Why does errno contain ENOTTY after a call to printf? A: Many implementations of the stdio package adjust their behavior slightly if stdout is a terminal. To make the determination, these implementations perform an operation which fails (with ENOTTY) if stdout is not a terminal. Although the output operation goes on to complete successfully, errno still contains ENOTTY. References: CT&P Sec. 5.4 p. 73. 11.8: My program's prompts and intermediate output don't always show up on the screen, especially when I pipe the output through another program. A: It is best to use an explicit fflush(stdout) whenever output should definitely be visible. Several mechanisms attempt to perform the fflush for you, at the "right time," but they tend to apply only when stdout is a terminal. (See question 11.7.) 11.9: When I read from the keyboard with scanf, it seems to hang until I type one extra line of input. A: scanf was designed for free-format input, which is seldom what you want when reading from the keyboard. In particular, "\n" in a format string does _not_ mean to expect a newline, but rather to read and discard characters as long as each is a whitespace character. A related problem is that unexpected non-numeric input can cause scanf to "jam." Because of these problems, it is usually better to use fgets to read a whole line, and then use sscanf or other string functions to pick apart the line buffer. If you do use sscanf, don't forget to check the return value to make sure that the expected number of items were found. 11.10: I'm trying to update a file in place, by using fopen mode "r+", then reading a certain string, and finally writing back a modified string, but it's not working. A: Be sure to call fseek before you write, both to seek back to the beginning of the string you're trying to overwrite, and because an fseek or fflush is always required between reading and writing in the read/write "+" modes. Also, remember that you can only overwrite characters with the same number of replacement characters; see also question 17.4. References: ANSI Sec. 4.9.5.3 p. 131. 11.11: How can I read one character at a time, without waiting for the RETURN key? A: See question 16.1. 11.12: How can I flush pending input so that a user's typeahead isn't read at the next prompt? Will fflush(stdin) work? A: fflush is defined only for output streams. Since its definition of "flush" is to complete the writing of buffered characters (not to discard them), discarding unread input would not be an analogous meaning for fflush on input streams. There is no standard way to discard unread characters from a stdio input buffer, nor would such a way be sufficient; unread characters can also accumulate in other, OS-level input buffers. 11.13: How can I redirect stdin or stdout to a file from within a program? A: Use freopen. 11.14: Once I've used freopen, how can I get the original stdout (or stdin) back? A: If you need to switch back and forth, the best all-around solution is not to use freopen in the first place. Try using your own explicit output (or input) stream variable, which you can reassign at will, while leaving the original stdout (or stdin) undisturbed. 11.15: How can I recover the file name given an open file descriptor? A: This problem is, in general, insoluble. Under Unix, for instance, a scan of the entire disk, (perhaps requiring special permissions) would theoretically be required, and would fail if the file descriptor was a pipe or referred to a deleted file (and could give a misleading answer for a file with multiple links). It is best to remember the names of files yourself when you open them (perhaps with a wrapper function around fopen). Section 12. Library Subroutines 12.1: Why does strncpy not always place a '\0' termination in the destination string? A: strncpy was first designed to handle a now-obsolete data structure, the fixed-length, not-necessarily-\0-terminated "string." strncpy is admittedly a bit cumbersome to use in other contexts, since you must often append a '\0' to the destination string by hand. 12.2: I'm trying to sort an array of strings with qsort, using strcmp as the comparison function, but it's not working. A: By "array of strings" you probably mean "array of pointers to char." The arguments to qsort's comparison function are pointers to the objects being sorted, in this case, pointers to pointers to char. (strcmp, of course, accepts simple pointers to char.) The comparison routine's arguments are expressed as "generic pointers," const void * or char *. They must be converted back to what they "really are" (char **) and dereferenced, yielding char *'s which can be usefully compared. Write a comparison function like this: int pstrcmp(p1, p2) /* compare strings through pointers */ char *p1, *p2; /* const void * for ANSI C */ { return strcmp(*(char **)p1, *(char **)p2); } Beware of the discussion in K&R II Sec. 5.11 pp. 119-20, which is not discussing Standard library qsort. 12.3: Now I'm trying to sort an array of structures with qsort. My comparison routine takes pointers to structures, but the compiler complains that the function is of the wrong type for qsort. How can I cast the function pointer to shut off the warning? A: The conversions must be in the comparison function, which must be declared as accepting "generic pointers" (const void * or char *) as discussed in question 12.2 above. The code might look like int mystructcmp(p1, p2) char *p1, *p2; /* const void * for ANSI C */ { struct mystruct *sp1 = (struct mystruct *)p1; struct mystruct *sp2 = (struct mystruct *)p2; /* now compare sp1->whatever and sp2-> ... */ } (If, on the other hand, you're sorting pointers to structures, you'll need indirection, as in question 12.2: sp1 = *(struct mystruct **)p1 .) 12.4: How can I convert numbers to strings (the opposite of atoi)? Is there an itoa function? A: Just use sprintf. (You'll have to allocate space for the result somewhere anyway; see questions 3.1 and 3.2. Don't worry that sprintf may be overkill, potentially wasting run time or code space; it works well in practice.) References: K&R I Sec. 3.6 p. 60; K&R II Sec. 3.6 p. 64. 12.5: How can I get the current date or time of day in a C program? A: Just use the time, ctime, and/or localtime functions. (These routines have been around for years, and are in the ANSI standard.) Here is a simple example: #include #include main() { time_t now = time((time_t *)NULL); printf("It's %.24s.\n", ctime(&now)); return 0; } References: ANSI Sec. 4.12 . 12.6: I know that the library routine localtime will convert a time_t into a broken-down struct tm, and that ctime will convert a time_t to a printable string. How can I perform the inverse operations of converting a struct tm or a string into a time_t? A: ANSI C specifies a library routine, mktime, which converts a struct tm to a time_t. Several public-domain versions of this routine are available in case your compiler does not support it yet. Converting a string to a time_t is harder, because of the wide variety of date and time formats which should be parsed. Some systems provide a strptime function; another popular routine is partime (widely distributed with the RCS package), but these are less likely to become standardized. References: K&R II Sec. B10 p. 256; H&S Sec. 20.4 p. 361; ANSI Sec. 4.12.2.3 . 12.7: How can I add n days to a date? How can I find the difference between two dates? A: The ANSI/ISO Standard C mktime and difftime functions provide some support for both problems. mktime() accepts non-normalized dates, so it is straightforward to take a filled-in struct tm, add or subtract from the tm_mday field, and call mktime() to normalize the year, month, and day fields (and convert to a time_t value). difftime() computes the difference, in seconds, between two time_t values; mktime() can be used to compute time_t values for two dates to be subtracted. (Note, however, that these solutions only work for dates in the range which can be represented as time_t's, and that not all days are 86400 seconds long.) See also questions 12.6 and 17.28. References: K&R II Sec. B10 p. 256; H&S Secs. 20.4, 20.5 pp. 361-362; ANSI Secs. 4.12.2.2, 4.12.2.3 . 12.8: I need a random number generator. A: The standard C library has one: rand(). The implementation on your system may not be perfect, but writing a better one isn't necessarily easy, either. References: ANSI Sec. 4.10.2.1 p. 154; Knuth Vol. 2 Chap. 3 pp. 1-177. 12.9: How can I get random integers in a certain range? A: The obvious way, rand() % N (where N is of course the range) is poor, because the low-order bits of many random number generators are distressingly non- random. (See question 12.11.) A better method is something like (int)((double)rand() / ((double)RAND_MAX + 1) * N) If you're worried about using floating point, you could try rand() / (RAND_MAX / N + 1) Both methods obviously require knowing RAND_MAX (which ANSI defines in ), and assume that N is much less than RAND_MAX. 12.10: Each time I run my program, I get the same sequence of numbers back from rand(). A: You can call srand() to seed the pseudo-random number generator with a more random initial value. Popular seed values are the time of day, or the elapsed time before the user presses a key (although keypress times are hard to determine portably; see question 16.10). References: ANSI Sec. 4.10.2.2 p. 154. 12.11: I need a random true/false value, so I'm taking rand() % 2, but it's just alternating 0, 1, 0, 1, 0... A: Poor pseudorandom number generators (such as the ones unfortunately supplied with some systems) are not very random in the low-order bits. Try using the higher-order bits. See question 12.9. 12.12: I'm trying to port this A: Those routines are variously old program. Why do I obsolete; you should get "undefined external" instead: errors for: index? use strchr. rindex? use strrchr. bcopy? use memmove, after interchanging the first and second arguments (see also question 5.15). bcmp? use memcmp. bzero? use memset, with a second argument of 0. 12.13: I keep getting errors due to library routines being undefined, but I'm #including all the right header files. A: In some cases (especially if the routines are nonstandard) you may have to explicitly ask for the correct libraries to be searched when you link the program. See also question 15.2. 12.14: I'm still getting errors due to library routines being undefined, even though I'm using -l to request the libraries while linking. A: Many linkers make one pass over the list of object files and libraries you specify, and extract from libraries only those modules which satisfy references which have so far come up as undefined. Therefore, the order in which libraries are listed with respect to object files (and each other) is significant; usually, you want to search the libraries last. (For example, under Unix, put any -l switches towards the end of the command line.) 12.15: I need some code to do regular expression matching. A: Look for the regexp library (supplied with many Unix systems), or get Henry Spencer's regexp package from cs.toronto.edu in pub/regexp.shar.Z (see also question 17.12). 12.16: How can I split up a command line into whitespace-separated arguments, like main's argc and argv? A: Most systems have a routine called strtok, although it can be tricky to use and it may not do everything you want it to (e.g., quoting). References: ANSI Sec. 4.11.5.8; K&R II Sec. B3 p. 250; H&S Sec. 15.7; PCS p. 178. Section 13. Lint 13.1: I just typed in this program, and it's acting strangely. Can you see anything wrong with it? A: Try running lint first (perhaps with the -a, -c, -h, -p and/or other options). Many C compilers are really only half- compilers, electing not to diagnose numerous source code difficulties which would not actively preclude code generation. 13.2: How can I shut off the "warning: possible pointer alignment problem" message lint gives me for each call to malloc? A: The problem is that traditional versions of lint do not know, and cannot be told, that malloc "returns a pointer to space suitably aligned for storage of any type of object." It is possible to provide a pseudoimplementation of malloc, using a #define inside of #ifdef lint, which effectively shuts this warning off, but a simpleminded #definition will also suppress meaningful messages about truly incorrect invocations. It may be easier simply to ignore the message, perhaps in an automated way with grep -v. 13.3: Where can I get an ANSI-compatible lint? A: A product called FlexeLint is available (in "shrouded source form," for compilation on 'most any system) from Gimpel Software 3207 Hogarth Lane Collegeville, PA 19426 USA (+1) 215 584 4261 The System V release 4 lint is ANSI-compatible, and is available separately (bundled with other C tools) from UNIX Support Labs or from System V resellers. In the absence of lint, many modern compilers attempt to diagnose almost as many problems as a good lint does. Section 14. Style 14.1: Here's a neat trick: if(!strcmp(s1, s2)) Is this good style? A: It is not particularly good style, although it is a popular idiom. The test succeeds if the two strings are equal, but its form suggests that it tests for inequality. Another solution is to use a macro: #define Streq(s1, s2) (strcmp((s1), (s2)) == 0) Opinions on code style, like those on religion, can be debated endlessly. Though good style is a worthy goal, and can usually be recognized, it cannot be codified. 14.2: What's the best style for code layout in C? A: K&R, while providing the example most often copied, also supply a good excuse for avoiding it: The position of braces is less important, although people hold passionate beliefs. We have chosen one of several popular styles. Pick a style that suits you, then use it consistently. It is more important that the layout chosen be consistent (with itself, and with nearby or common code) than that it be "perfect." If your coding environment (i.e. local custom or company policy) does not suggest a style, and you don't feel like inventing your own, just copy K&R. (The tradeoffs between various indenting and brace placement options can be exhaustively and minutely examined, but don't warrant repetition here. See also the Indian Hill Style Guide.) The elusive quality of "good style" involves much more than mere code layout details; don't spend time on formatting to the exclusion of more substantive code quality issues. References: K&R Sec. 1.2 p. 10. 14.3: Where can I get the "Indian Hill Style Guide" and other coding standards? A: Various documents are available for anonymous ftp from: Site: File or directory: cs.washington.edu ~ftp/pub/cstyle.tar.Z (128.95.1.4) (the updated Indian Hill guide) cs.toronto.edu doc/programming ftp.cs.umd.edu pub/style-guide Section 15. Floating Point 15.1: My floating-point calculations are acting strangely and giving me different answers on different machines. A: First, make sure that you have #included , and correctly declared other functions returning double. If the problem isn't that simple, recall that most digital computers use floating-point formats which provide a close but by no means exact simulation of real number arithmetic. Underflow, cumulative precision loss, and other anomalies are often troublesome. Don't assume that floating-point results will be exact, and especially don't assume that floating-point values can be compared for equality. (Don't throw haphazard "fuzz factors" in, either.) These problems are no worse for C than they are for any other computer language. Floating-point semantics are usually defined as "however the processor does them;" otherwise a compiler for a machine without the "right" model would have to do prohibitively expensive emulations. This article cannot begin to list the pitfalls associated with, and workarounds appropriate for, floating-point work. A good programming text should cover the basics. References: EoPS Sec. 6 pp. 115-8. 15.2: I'm trying to do some simple trig, and I am #including , but I keep getting "undefined: _sin" compilation errors. A: Make sure you're linking with the correct math library. For instance, under Unix, you usually need to use the -lm option, and at the _end_ of the command line, when compiling/linking. See also question 12.14. 15.3: Why doesn't C have an exponentiation operator? A: Because few processors have an exponentiation instruction. Instead, you can #include and use the pow() function, although explicit multiplication is often better for small positive integral exponents. References: ANSI Sec. 4.5.5.1 . 15.4: How do I round numbers? A: The simplest and most straightforward way is with code like (int)(x + 0.5) This won't work properly for negative numbers, though. 15.5: How do I test for IEEE NaN and other special values? A: Many systems with high-quality IEEE floating-point implementations provide facilities (e.g. an isnan() macro) to deal with these values cleanly, and the Numerical C Extensions Group (NCEG) is working to formally standardize such facilities. A crude but usually effective test for NaN is exemplified by #define isnan(x) ((x) != (x)) although non-IEEE-aware compilers may optimize the test away. 15.6: I'm having trouble with a Turbo C program which crashes and says something like "floating point formats not linked." A: Some compilers for small machines, including Turbo C (and Ritchie's original PDP-11 compiler), leave out floating point support if it looks like it will not be needed. In particular, the non-floating-point versions of printf and scanf save space by not including code to handle %e, %f, and %g. It happens that Turbo C's heuristics for determining whether the program uses floating point are insufficient, and the programmer must sometimes insert an extra, explicit call to a floating-point library routine to force loading of floating-point support. Section 16. System Dependencies 16.1: How can I read a single character from the keyboard without waiting for a newline? A: Contrary to popular belief and many people's wishes, this is not a C-related question. (Nor are closely-related questions concerning the echo of keyboard input.) The delivery of characters from a "keyboard" to a C program is a function of the operating system in use, and has not been standardized by the C language. Some versions of curses have a cbreak() function which does what you want. If you're specifically trying to read a short password without echo, you might try getpass(). Under Unix, use ioctl to play with the terminal driver modes (CBREAK or RAW under "classic" versions; ICANON, c_cc[VMIN] and c_cc[VTIME] under System V or Posix systems). Under MS-DOS, use getch(). Under VMS, try the Screen Management (SMG$) routines, or curses, or issue low-level $QIO's with the IO$_READVBLK (and perhaps IO$M_NOECHO) function codes to ask for one character at a time. Under other operating systems, you're on your own. Beware that some operating systems make this sort of thing impossible, because character collection into input lines is done by peripheral processors not under direct control of the CPU running your program. Operating system specific questions are not appropriate for comp.lang.c . Many common questions are answered in frequently-asked questions postings in such groups as comp.unix.questions and comp.os.msdos.programmer . Note that the answers are often not unique even across different variants of a system; bear in mind when answering system-specific questions that the answer that applies to your system may not apply to everyone else's. References: PCS Sec. 10 pp. 128-9, Sec. 10.1 pp. 130-1. 16.2: How can I find out if there are characters available for reading (and if so, how many)? Alternatively, how can I do a read that will not block if there are no characters available? A: These, too, are entirely operating-system-specific. Some versions of curses have a nodelay() function. Depending on your system, you may also be able to use "nonblocking I/O", or a system call named "select", or the FIONREAD ioctl, or kbhit(), or rdchk(), or the O_NDELAY option to open() or fcntl(). 16.3: How can I clear the screen? How can I print things in inverse video? A: Such things depend on the terminal type (or display) you're using. You will have to use a library such as termcap or curses, or some system-specific routines, to perform these functions. 16.4: How do I read the mouse? A: Consult your system documentation, or ask on an appropriate system-specific newsgroup (but check its FAQ list first). Mouse handling is completely different under the X window system, MS- DOS, Macintosh, and probably every other system. 16.5: How can my program discover the complete pathname to the executable file from which it was invoked? A: argv[0] may contain all or part of the pathname, or it may contain nothing. You may be able to duplicate the command language interpreter's search path logic to locate the executable if the name in argv[0] is present but incomplete. However, there is no guaranteed or portable solution. 16.6: How can a process change an environment variable in its caller? A: In general, it cannot. Different operating systems implement name/value functionality similar to the Unix environment in different ways. Whether the "environment" can be usefully altered by a running program, and if so, how, is system- dependent. Under Unix, a process can modify its own environment (some systems provide setenv() and/or putenv() functions to do this), and the modified environment is usually passed on to any child processes, but it is _not_ propagated back to the parent process. 16.7: How can I check whether a file exists? I want to query the user before overwriting existing files. A: On Unix-like systems, you can try the access() routine, although it's got a few problems. (It isn't atomic with respect to the following action, and can have anomalies if used in setuid programs.) Another option (perhaps preferable) is to call stat() on the file. Otherwise, the only guaranteed and portable way to test for file existence is to try opening the file (which doesn't help if you're trying to avoid overwriting an existing file, unless you've got something like the BSD Unix O_EXCL open option available). 16.8: How can I find out the size of a file, prior to reading it in? A: If the "size of a file" is the number of characters you'll be able to read from it in C, it is in general impossible to determine this number in advance. Under Unix, the stat call will give you an exact answer, and several other systems supply a Unix-like stat which will give an approximate answer. You can fseek to the end and then use ftell, but this usage is nonportable (it gives you an accurate answer only under Unix, and otherwise a quasi-accurate answer only for ANSI C "binary" files). Some systems provide routines called filesize or filelength. Are you sure you have to determine the file's size in advance? Since the most accurate way of determining the size of a file as a C program will see it is to open the file and read it, perhaps you can rearrange the code to learn the size as it reads. 16.9: How can a file be shortened in-place without completely clearing or rewriting it? A: BSD systems provide ftruncate(), several others supply chsize(), and a few may provide a (possibly undocumented) fcntl option F_FREESP. Under MS-DOS, you can sometimes use write(fd, "", 0). However, there is no truly portable solution. 16.10: How can I implement a delay, or time a user's response, with sub-second resolution? A: Unfortunately, there is no portable way. V7 Unix, and derived systems, provided a fairly useful ftime() routine with resolution up to a millisecond, but it has disappeared from System V and Posix. Other routines you might look for on your system include nap(), setitimer(), msleep(), usleep(), clock(), and gettimeofday(). The select() and poll() calls (if available) can be pressed into service to implement simple delays. On MS-DOS machines, it is possible to reprogram the system timer and timer interrupts. 16.11: How can I read in an object file and jump to routines in it? A: You want a dynamic linker and/or loader. It is possible to malloc some space and read in object files, but you have to know an awful lot about object file formats, relocation, etc. Under BSD Unix, you could use system() and ld -A to do the linking for you. Many (most?) versions of SunOS and System V have the -ldl library which allows object files to be dynamically loaded. There is also a GNU package called "dld". See also question 7.6. 16.12: How can I invoke an operating system command from within a program? A: Use system(). References: K&R II Sec. B6 p. 253; ANSI Sec. 4.10.4.5; H&S Sec. 21.2; PCS Sec. 11 p. 179; 16.13: How can I invoke an operating system command and trap its output? A: Unix and some other systems provide a popen() routine, which sets up a stdio stream on a pipe connected to the process running a command, so that the output can be read (or the input supplied). Alternately, invoke the command simply (see question 16.12) in such a way that it writes its output to a file, then open and read that file. References: PCS Sec. 11 p. 169 . 16.14: How can I read a directory in a C program? A: See if you can use the opendir() and readdir() routines, which are available on most Unix systems. Implementations also exist for MS-DOS, VMS, and other systems. (MS-DOS also has FINDFIRST and FINDNEXT routines which do essentially the same thing.) 16.15: How can I do serial ("comm") port I/O? A: It's system-dependent. Under Unix, you typically open, read, and write a device in /dev, and use the facilities of the terminal driver to adjust its characteristics. Under MS-DOS, you can either use some primitive BIOS interrupts, or (if you require decent performance) one of any number of interrupt- driven serial I/O packages. Section 17. Miscellaneous 17.1: What can I safely assume about the initial values of variables which are not explicitly initialized? If global variables start out as "zero," is that good enough for null pointers and floating-point zeroes? A: Variables with "static" duration (that is, those declared outside of functions, and those declared with the storage class static), are guaranteed initialized (just once, at program startup) to zero, as if the programmer had typed "= 0". Therefore, such variables are initialized to the null pointer (of the correct type; see also Section 1) if they are pointers, and to 0.0 if they are floating-point. Variables with "automatic" duration (i.e. local variables without the static storage class) start out containing garbage, unless they are explicitly initialized. Nothing useful can be predicted about the garbage. Dynamically-allocated memory obtained with malloc and realloc is also likely to contain garbage, and must be initialized by the calling program, as appropriate. Memory obtained with calloc contains all-bits-0, but this is not necessarily useful for pointer or floating-point values (see question 3.13, and section 1). 17.2: This code, straight out of a book, isn't compiling: f() { char a[] = "Hello, world!"; } A: Perhaps you have a pre-ANSI compiler, which doesn't allow initialization of "automatic aggregates" (i.e. non-static local arrays and structures). As a workaround, you can make the array global or static, and initialize it with strcpy when f is called. (You can always initialize local char * variables with string literals, but see question 17.20). See also questions 5.16 and 5.17. 17.3: How can I write data files which can be read on other machines with different word size, byte order, or floating point formats? A: The best solution is to use text files (usually ASCII), written with fprintf and read with fscanf or the like. (Similar advice also applies to network protocols.) Be skeptical of arguments which imply that text files are too big, or that reading and writing them is too slow. Not only is their efficiency frequently acceptable in practice, but the advantages of being able to manipulate them with standard tools can be overwhelming. If you must use a binary format, you can improve portability, and perhaps take advantage of prewritten I/O libraries, by making use of standardized formats such as Sun's XDR (RFC 1014), OSI's ASN.1, CCITT's X.409, or ISO 8825 "Basic Encoding Rules." See also question 9.11. 17.4: How can I insert or delete a line (or record) in the middle of a file? A: Short of rewriting the file, you probably can't. See also question 16.9. 17.5: How can I return several values from a function? A: Either pass pointers to locations which the function can fill in, or have the function return a structure containing the desired values, or (in a pinch) consider global variables. See also questions 2.17, 3.4, and 9.2. 17.6: If I have a char * variable pointing to the name of a function as a string, how can I call that function? A: The most straightforward thing to do is maintain a correspondence table of names and function pointers: int function1(), function2(); struct {char *name; int (*funcptr)(); } symtab[] = { "function1", function1, "function2", function2, }; Then, just search the table for the name, and call through the associated function pointer. See also questions 9.9 and 16.11. 17.7: I seem to be missing the system header file . Can someone send me a copy? A: Standard headers exist in part so that definitions appropriate to your compiler, operating system, and processor can be supplied. You cannot just pick up a copy of someone else's header file and expect it to work, unless that person is using exactly the same environment. Ask your compiler vendor why the file was not provided (or to send a replacement copy). 17.8: How can I call FORTRAN (C++, BASIC, Pascal, Ada, LISP) functions from C? (And vice versa?) A: The answer is entirely dependent on the machine and the specific calling sequences of the various compilers in use, and may not be possible at all. Read your compiler documentation very carefully; sometimes there is a "mixed-language programming guide," although the techniques for passing arguments and ensuring correct run-time startup are often arcane. More information may be found in FORT.gz by Glenn Geers, available via anonymous ftp from suphys.physics.su.oz.au in the src directory. cfortran.h, a C header file, simplifies C/FORTRAN interfacing on many popular machines. It is available via anonymous ftp from zebra.desy.de (131.169.2.244). In C++, a "C" modifier in an external function declaration indicates that the function is to be called using C calling conventions. 17.9: Does anyone know of a program for converting Pascal or FORTRAN (or LISP, Ada, awk, "Old" C, ...) to C? A: Several public-domain programs are available: p2c A Pascal to C converter written by Dave Gillespie, posted to comp.sources.unix in March, 1990 (Volume 21); also available by anonymous ftp from csvax.cs.caltech.edu, file pub/p2c-1.20.tar.Z . ptoc Another Pascal to C converter, this one written in Pascal (comp.sources.unix, Volume 10, also patches in Volume 13?). f2c A Fortran to C converter jointly developed by people from Bell Labs, Bellcore, and Carnegie Mellon. To find out more about f2c, send the mail message "send index from f2c" to netlib@research.att.com or research!netlib. (It is also available via anonymous ftp on netlib.att.com, in directory netlib/f2c.) This FAQ list's maintainer also has available a list of other commercial translation products, and some for more obscure languages. See also question 5.3. 17.10: Is C++ a superset of C? Can I use a C++ compiler to compile C code? A: C++ was derived from C, and is largely based on it, but there are some legal C constructs which are not legal C++. (Many C programs will nevertheless compile correctly in a C++ environment.) 17.11: I need: A: Look for programs (see also question 17.12) named: a C cross-reference cflow, calls, cscope generator a C beautifier/pretty- cb, indent printer 17.12: Where can I get copies of all these public-domain programs? A: If you have access to Usenet, see the regular postings in the comp.sources.unix and comp.sources.misc newsgroups, which describe, in some detail, the archiving policies and how to retrieve copies. The usual approach is to use anonymous ftp and/or uucp from a central, public-spirited site, such as uunet (ftp.uu.net, 192.48.96.9). However, this article cannot track or list all of the available archive sites and how to access them. Ajay Shah maintains an index of free numerical software; it is posted periodically, and available where this FAQ list is archived (see question 17.33). The comp.archives newsgroup contains numerous announcements of anonymous ftp availability of various items. The "archie" mailserver can tell you which anonymous ftp sites have which packages; send the mail message "help" to archie@quiche.cs.mcgill.ca for information. Finally, the newsgroup comp.sources.wanted is generally a more appropriate place to post queries for source availability, but check _its_ FAQ list, "How to find sources," before posting there. 17.13: When will the next International Obfuscated C Code Contest (IOCCC) be held? How can I get a copy of the current and previous winning entries? A: The contest typically runs from early March through mid-May. To obtain a current copy of the rules and guidelines, send e-mail with the Subject: line "send rules" to: {apple,pyramid,sun,uunet}!hoptoad!judges or judges@toad.com (Note that these are _not_ the addresses for submitting entries.) Contest winners are first announced at the Summer Usenix Conference in mid-June, and posted to the net sometime in July- August. Winning entries from previous years (to 1984) are archived at uunet (see question 17.12) under the directory ~/pub/ioccc. As a last resort, previous winners may be obtained by sending e-mail to the above address, using the Subject: "send YEAR winners", where YEAR is a single four-digit year, a year range, or "all". 17.14: Why don't C comments nest? How am I supposed to comment out code containing comments? Are comments legal inside quoted strings? A: Nested comments would cause more harm than good, mostly because of the possibility of accidentally leaving comments unclosed by including the characters "/*" within them. For this reason, it is usually better to "comment out" large sections of code, which might contain comments, with #ifdef or #if 0 (but see question 5.11). The character sequences /* and */ are not special within double-quoted strings, and do not therefore introduce comments, because a program (particularly one which is generating C code as output) might want to print them. References: ANSI Appendix E p. 198, Rationale Sec. 3.1.9 p. 33. 17.15: How can I get the ASCII value corresponding to a character, or vice versa? A: In C, characters are represented by small integers corresponding to their values (in the machine's character set) so you don't need a conversion routine: if you have the character, you have its value. 17.16: How can I implement sets and/or arrays of bits? A: Use arrays of char or int, with a few macros to access the right bit at the right index (try using 8 for CHAR_BIT if you don't have ): #include /* for CHAR_BIT */ #define BITMASK(bit) (1 << ((bit) % CHAR_BIT)) #define BITSLOT(bit) ((bit) / CHAR_BIT) #define BITSET(ary, bit) ((ary)[BITSLOT(bit)] |= BITMASK(bit)) #define BITTEST(ary, bit) ((ary)[BITSLOT(bit)] & BITMASK(bit)) 17.17: What is the most efficient way to count the number of bits which are set in a value? A: This and many other similar bit-twiddling problems can often be sped up and streamlined using lookup tables (but see the next question). 17.18: How can I make this code more efficient? A: Efficiency, though a favorite comp.lang.c topic, is not important nearly as often as people tend to think it is. Most of the code in most programs is not time-critical. When code is not time-critical, it is far more important that it be written clearly and portably than that it be written maximally efficiently. (Remember that computers are very, very fast, and that even "inefficient" code can run without apparent delay.) It is notoriously difficult to predict what the "hot spots" in a program will be. When efficiency is a concern, it is important to use profiling software to determine which parts of the program deserve attention. Often, actual computation time is swamped by peripheral tasks such as I/O and memory allocation, which can be sped up by using buffering and caching techniques. For the small fraction of code that is time-critical, it is vital to pick a good algorithm; it is less important to "microoptimize" the coding details. Many of the "efficient coding tricks" which are frequently suggested (e.g. substituting shift operators for multiplication by powers of two) are performed automatically by even simpleminded compilers. Heavyhanded "optimization" attempts can make code so bulky that performance is degraded. For more discussion of efficiency tradeoffs, as well as good advice on how to increase efficiency when it is important, see chapter 7 of Kernighan and Plauger's The Elements of Programming Style, and Jon Bentley's Writing Efficient Programs. 17.19: Are pointers really faster than arrays? How much do function calls slow things down? Is ++i faster than i = i + 1? A: Precise answers to these and many similar questions depend of course on the processor and compiler in use. If you simply must know, you'll have to time test programs carefully. (Often the differences are so slight that hundreds of thousands of iterations are required even to see them. Check the compiler's assembly language output, if available, to see if two purported alternatives aren't compiled identically.) It is "usually" faster to march through large arrays with pointers rather than array subscripts, but for some processors the reverse is true. Function calls, though obviously incrementally slower than in- line code, contribute so much to modularity and code clarity that there is rarely good reason to avoid them. Before rearranging expressions such as i = i + 1, remember that you are dealing with a C compiler, not a keystroke-programmable calculator. Any decent compiler will generate identical code for ++i, i += 1, and i = i + 1. The reasons for using ++i or i += 1 over i = i + 1 have to do with style, not efficiency. (See also question 4.7.) 17.20: Why does this code: char *p = "Hello, world!"; p[0] = tolower(p[0]); crash? A: String literals are not necessarily modifiable, except (in effect) when they are used as array initializers. Try char a[] = "Hello, world!"; (For compiling old code, some compilers have a switch controlling whether strings are writable or not.) See also questions 2.1, 2.2, 2.8, and 17.2. References: ANSI Sec. 3.1.4 . 17.21: This program crashes before it even runs! (When single-stepping with a debugger, it dies before the first statement in main.) A: You probably have one or more very large (kilobyte or more) local arrays. Many systems have fixed-size stacks, and those which perform dynamic stack allocation automatically (e.g. Unix) can be confused when the stack tries to grow by a huge chunk all at once. It is often better to declare large arrays with static duration (unless of course you need a fresh set with each recursive call). (See also question 9.4.) 17.22: What do "Segmentation violation" and "Bus error" mean? A: These generally mean that your program tried to access memory it shouldn't have, invariably as a result of improper pointer use, often involving uninitialized or improperly allocated pointers (see questions 3.1 and 3.2), or malloc (see question 17.23), or perhaps scanf (see question 11.3). 17.23: My program is crashing, apparently somewhere down inside malloc, but I can't see anything wrong with it. A: It is unfortunately very easy to corrupt malloc's internal data structures, and the resulting problems can be hard to track down. The most common source of problems is writing more to a malloc'ed region than it was allocated to hold; a particularly common bug is to malloc(strlen(s)) instead of strlen(s) + 1. Other problems involve freeing pointers not obtained from malloc, or trying to realloc a null pointer (see question 3.12). A number of debugging packages exist to help track down malloc problems; one popular one is Conor P. Cahill's "dbmalloc," posted to comp.sources.misc in September of 1992. Others are "leak," available in volume 27 of the comp.sources.unix archives; JMalloc.c and JMalloc.h in Fidonet's C_ECHO Snippets (or ask archie; see question 17.12); and MEMDEBUG from ftp.crpht.lu in pub/sources/memdebug . See also question 17.12. 17.24: Does anyone have a C compiler test suite I can use? A: Plum Hall (formerly in Cardiff, NJ; now in Hawaii) sells one. The FSF's GNU C (gcc) distribution includes a c-torture- test.tar.Z which checks a number of common problems with compilers. Kahan's paranoia test, found in netlib/paranoia on netlib.att.com, strenuously tests a C implementation's floating point capabilities. 17.25: Where can I get a YACC grammar for C? A: The definitive grammar is of course the one in the ANSI standard. Another grammar, by Jim Roskind, is in pub/*grammar* at ics.uci.edu . A fleshed-out, working instance of the ANSI grammar (due to Jeff Lee) is on uunet (see question 17.12) in usenet/net.sources/ansi.c.grammar.Z (including a companion lexer). The FSF's GNU C compiler contains a grammar, as does the appendix to K&R II. References: ANSI Sec. A.2 . 17.26: I need code to parse and evaluate expressions. A: Two available packages are "defunc," posted to comp.source.misc in December, 1993 (V41 i32,33), to alt.sources in January, 1994, and available from sunsite.unc.edu in pub/packages/development/libraries/defunc-1.3.tar.Z; and "parse," at lamont.ldgo.columbia.edu. 17.27: I need a sort of an "approximate" strcmp routine, for comparing two strings for close, but not necessarily exact, equality. A: The traditional routine for doing this sort of thing involves the "soundex" algorithm, which maps similar-sounding words to the same numeric codes. Soundex is described in the Searching and Sorting volume of Donald Knuth's classic _The Art of Computer Programming_. 17.28: How can I find the day of the week given the date? A: Use mktime (see questions 12.6 and 12.7), or Zeller's congruence, or see the sci.math FAQ list, or try this code posted by Tomohiko Sakamoto: dayofweek(y, m, d) /* 0 = Sunday */ int y, m, d; /* 1 <= m <= 12, y > 1752 or so */ { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; } 17.29: Will 2000 be a leap year? Is (year % 4 == 0) an accurate test for leap years? A: Yes and no, respectively. The full expression for the Gregorian calendar is year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) See a good astronomical almanac or other reference for details. 17.30: How do you pronounce "char"? A: You can pronounce the C keyword "char" in at least three ways: like the English words "char," "care," or "car;" the choice is arbitrary. 17.31: What's a good book for learning C? A: Mitch Wright maintains an annotated bibliography of C and Unix books; it is available for anonymous ftp from ftp.rahul.net in directory pub/mitch/YABL. This FAQ list's editor maintains a collection of previous answers to this question, which is available upon request. 17.32: Are there any C tutorials on the net? A: There are at least two of them: "Notes for C programmers," by Christopher Sawtell, available from: svr-ftp.eng.cam.ac.uk:misc/sawtell_C.shar garbo.uwasa.fi:/pc/c-lang/c-lesson.zip paris7.jussieu.fr:/contributions/docs Tim Love's "C for Programmers," available from svr-ftp.eng.cam.ac.uk in the misc directory. 17.33: Where can I get extra copies of this list? What about back issues? A: For now, just pull it off the net; it is normally posted to comp.lang.c on the first of each month, with an Expires: line which should keep it around all month. An abridged version is also available (and posted), as is a list of changes accompanying each significantly updated version. These lists can also be found in the newsgroups comp.answers and news.answers . Several sites archive news.answers postings and other FAQ lists, including this one; two sites are rtfm.mit.edu (directories pub/usenet/news.answers/C-faq/ and pub/usenet/comp.lang.c/ ) and ftp.uu.net (directory usenet/news.answers/C-faq/ ). The archie server should help you find others; query it for "prog C-faq". See the meta-FAQ list in news.answers for more information; see also question 17.12. This list is an evolving document of questions which have been Frequent since before the Great Renaming, not just a collection of this month's interesting questions. Older copies are obsolete and don't contain much, except the occasional typo, that the current list doesn't. Bibliography ANSI American National Standard for Information Systems -- Programming Language -- C, ANSI X3.159-1989 (see question 5.2). JLB Jon Louis Bentley, Writing Efficient Programs, Prentice-Hall, 1982, ISBN 0-13-970244-X. H&S Samuel P. Harbison and Guy L. Steele, C: A Reference Manual, Second Edition, Prentice-Hall, 1987, ISBN 0-13-109802-0. (A third edition has recently been released.) PCS Mark R. Horton, Portable C Software, Prentice Hall, 1990, ISBN 0-13-868050-7. EoPS Brian W. Kernighan and P.J. Plauger, The Elements of Programming Style, Second Edition, McGraw-Hill, 1978, ISBN 0-07-034207-5. K&R I Brian W. Kernighan and Dennis M. Ritchie, The C Programming Language, Prentice-Hall, 1978, ISBN 0-13-110163-3. K&R II Brian W. Kernighan and Dennis M. Ritchie, The C Programming Language, Second Edition, Prentice Hall, 1988, ISBN 0-13- 110362-8, 0-13-110370-9. Knuth Donald E. Knuth, The Art of Computer Programming, (3 vols.), Addison-Wesley, 1981. CT&P Andrew Koenig, C Traps and Pitfalls, Addison-Wesley, 1989, ISBN 0-201-17928-8. P.J. Plauger, The Standard C Library, Prentice Hall, 1992, ISBN 0-13-131509-9. Harry Rabinowitz and Chaim Schaap, Portable C, Prentice-Hall, 1990, ISBN 0-13-685967-4. There is a more extensive bibliography in the revised Indian Hill style guide (see question 14.3). See also question 17.31. Acknowledgements Thanks to Jamshid Afshar, Sudheer Apte, Randall Atkinson, Dan Bernstein, Vincent Broman, Stan Brown, Joe Buehler, Gordon Burditt, Burkhard Burow, Conor P. Cahill, D'Arcy J.M. Cain, Christopher Calabrese, Ian Cargill, Paul Carter, Billy Chambless, Raymond Chen, Jonathan Coxhead, Lee Crawford, Steve Dahmer, Andrew Daviel, James Davies, Jutta Degener, Norm Diamond, Jeff Dunlop, Ray Dunn, Stephen M. Dunn, Michael J. Eager, Dave Eisen, Bjorn Engsig, Chris Flatters, Rod Flores, Alexander Forst, Jeff Francis, Dave Gillespie, Samuel Goldstein, Alasdair Grant, Ron Guilmette, Doug Gwyn, Tony Hansen, Joe Harrington, Guy Harris, Elliotte Rusty Harold, Jos Horsmeier, Blair Houghton, Ke Jin, Kirk Johnson, Larry Jones, Kin-ichi Kitano, Peter Klausler, Andrew Koenig, Tom Koenig, Ajoy Krishnan T, Markus Kuhn, John Lauro, Felix Lee, Mike Lee, Timothy J. Lee, Tony Lee, Don Libes, Christopher Lott, Tim Love, Tim McDaniel, Stuart MacMartin, John R. MacMillan, Bob Makowski, Evan Manning, Barry Margolin, George Matas, Brad Mears, Bill Mitchell, Mark Moraes, Darren Morby, Ken Nakata, Landon Curt Noll, David O'Brien, Richard A. O'Keefe, Hans Olsson, Philip (lijnzaad@embl-heidelberg.de), Andrew Phillips, Christopher Phillips, Francois Pinard, Dan Pop, Kevin D. Quitt, Pat Rankin, J. M. Rosenstock, Erkki Ruohtula, Tomohiko Sakamoto, Rich Salz, Chip Salzenberg, Paul Sand, DaviD W. Sanderson, Christopher Sawtell, Paul Schlyter, Doug Schmidt, Rene Schmit, Russell Schulz, Patricia Shanahan, Peter da Silva, Joshua Simons, Henry Spencer, David Spuler, Melanie Summit, Erik Talvola, Clarke Thatcher, Wayne Throop, Chris Torek, Andrew Tucker, Goran Uddeborg, Rodrigo Vanegas, Jim Van Zandt, Wietse Venema, Ed Vielmetti, Larry Virden, Chris Volpe, Mark Warren, Larry Weiss, Freek Wiedijk, Lars Wirzenius, Dave Wolverton, Mitch Wright, Conway Yee, and Zhuo Zang, who have contributed, directly or indirectly, to this article. Special thanks to Karl Heuer, and particularly to Mark Brader, who (to borrow a line from Steve Johnson) have goaded me beyond my inclination, and occasionally beyond my endurance, in relentless pursuit of a better FAQ list. Steve Summit scs@eskimo.com This article is Copyright 1988, 1990-1994 by Steve Summit. It may be freely redistributed so long as the author's name, and this notice, are retained. The C code in this article (vstrcat(), error(), etc.) is public domain and may be used without restriction. .  fr.. FOOTBALL  fs.  fs..  frFB2 DAT MevtFB3 DAT Uevp&FB4 DAT \evxFOOTBALLGFA Fg$ ``88??  pp``@@``bF@e@ppp 0 0 0 0 0 0l6`l6`}} ??  pp``@@``ebFppp 0 0 0 0 0 0l6`l6`}} ``00  pppp00F@@pppl0`0l`000066}} `` 8 8  pp``@@` ` b@@e@ppp 6 6  }  }l`l`   pppp00Fpppl0`0l`000066}}   pp``@@` ` eb@ppp 6 6  }  }l`l`@@ @88p|p|@@eb@g`rrrx=h7ph5z>~>~>z>@@@ @ @@```````pp00pp@```````7`37`3Uww]www`7`7>>FpppN8N8@N8????|^|~|~|^8x8x8x8x8?????ggGGgge@bF@ppp 0 0 0 0 0 0l6`l6`}} ?????ggGGggbFeppp 0 0 0 0 0 0l6`l6`}}0??ww77@F@pppl`0l0`000066}} 8??ggGGgge@b@@ppp6  6 }   }l`l` ??ww77Fpppl`0l0`000066}} ??ggGGggb@eppp6  6 }   }l`l``` >>wwGGb@e`grrrp h7x?h5z>~>~>z>``@ @@```````p0p@```````7`37`3Uww]www`@7|`7|??Fppp@N8N8N8????|^|~|~|^8x8x8x8x@@ 0 ???*??  ggGGggggppp 0 0 0 0 0 0l6`l6`}} ` ???*??  ggGGggggppp 0 0 0 0 0 0l6`l6`}}@@ 0 ??   ww77pppl0`0l0`00066}}@@ 0P??PPgg@GG@gg@ggppp 6  6 }  }l`l` ` ??   ww77pppl0`0l0`00066}} `PP??PPgg@GG@gg@ggppp 6  6 }  }l`l`@   >>wwGGgg@ggrrrx=h7x?`z>~>~>z>@ ` `   @``````????` @PpPpP@ `````````7`37`3Uww]www????` @PP7|`7|??@@pppN8N8N8@????|^|~|~|^8x8x8x8x0 0 993333775777:\:\?8?(  ppp 0 0 0 0 0 0l6`l6`}}0 0 9933333374,76l;;??   ppp 0 0 0 0 0 0l6`l6`}}``77egggrNrN?8?PP@@@ppp 0 0 0 0 0 0l6`l6`}}``33gd&gffss??PPP@@@ppp 0 0 0 0 0 0l6`l6`}}@@<< ~ ~ppp0088000 0B0~0<00~||||pppp  << ~~ xpp8088|0 0B0~<000<0<pp11?? c`yyyx00ppppp0wxw8?78~78Z7330~3o{|k 0 0}~}>>>>  ````  @x@x``````0?  HHHppPP      `  ,0 0 >~||||0000??88-@?@33 88 p ???-???0000 hxxh8xxsxsxs0ppapapa0wxHw8?787877747o{|k 0 0}~}>>>> <<~~ >  B ~ << < ppP P     `~%Z~ 0 0 >~||||0000  ````<<~~    B ~< << p 3330 iyyhxxxxppppp0wxw8?78~78Z3oo330~3o{|k 0 0}~}>>>>  ````xx !pq88`ahh0 xx!?p HHH@      `  0 0 >~||||0000??-@?@33 ???-???0000hxxh8xxsxsxs0ppapapa0wxw8?787873307o{|k 0 0}~}>>>> @@<< ~ ~ppp0088000 0~0B0<00>>>  ````  x@@x``````0  xxxx>  HHHppP P    o ,< <6~||||0000??88m?@?@33 8800 (p8| ??-???0000 hxxh8xsxsxxs0papappa0xOww887?7874777l{k<0<~}}>>>> <<~~ >  ~ B << < pp PP<<>>   o[ ́< <6~||||0000  ````<<~~    ~ B< << p  0303< <hyxi88xxxxppppp0xww887?78[70333l{k<0<~}}>>>>  ````xx! qp88a`hh0 xxxxxxx!8ppp HHH@     o < <6~||||0000??m?@?@33 ??-???0000hxxh8xsxsxxs0papappa0xww887?7870337l{k<0<~}}>>>>  888|||||||||\|}||}||}|8988888||q|q|q||||||||||||8888||8  L|?..   x`x`???<?<???323 ?<? @@`ccgpl|oxww8;;;8888;; & `gggxxxxt8|8t8pppp0000=??=????xx{88;&  gg`gxxx|8t8xt8pppp0000=??=????`  8p8p@?@?c0cg g ?`? Cww>..   x`x`;8;xc8c8??F@@ppp 0 0 0 0 0 0l6`l6`}}C@ͳ88C<<:>:>>?8C<><^<J<><<<<<8$@t@`,`<<<=<>>,<0`D`D@D@DEDFFDDx`_ `PPD܀D E DEE@@``pnA\]p|s܀d9܀d|?@||p|g3g||4|4H4|4H5}4H5}4H4|4H4|4H  RRRPPP||||||n|n|n|n|n|n|ftftftftftftfdfdfdfdfdfdffffffffffffffffffopp<<<<<<<<<<||||||||||||||||||||<|`|`|`|`|`x~~~~~~~~~~`z`z~pzz`~`z`@@?  @?@@@?:?8><:>:>>?8~B<><^<J<><<<<<8$a`Au@,<<<=<>>,< @xDD@D@DEDFFDDXXPPDԀDEDEEPP@@G8p\\]p|dqd9܀d|~~||gg3g||$|$X$|$X%}$X%}$X$|$X$|$XRRRPPP>n>n?n>n>n>n n ^ n ^ n ^ n ^ n ^ n ^ f V f V f V f V f V f Vffffffffffffnnn nnn nnn nnn nnn nnn P2hP2hOpti@@@@@@@@@@ @@@@@C888888Î888888C888888@@@` P888888 888 @  `888888`HHH(((QQQ>080888||888>>||||pxxx<<<sss"p"p"prrr < < <    aaar r r y y y<<"pp"p< < P2 @ C888888C888888C888888@888@888888   00 `080888888` ///aaappp((("""((( ((("""((( ppρqqqȀȀȀ pp P2(H( H Cxx8888Îxx8888C88888` ÎPÎÎ  ``@`` @@@@@@@@@` 8C8C8888yyy   <<<ppp888ppp   aaa   >>>```ǢǢǢqqq<<<GǢGP2jbP2jbcreen@@@@0@P@ @  @  @  @  @@@@@@@@@@@@@@@C888888@@C888888C888888@@(8(888 998888998888 @@@ @@@ `aaa  "p"p"p888""" aaa "" " >>>``` qqqqqqP2Ā@ @     888!888!888!  8 (8(8@8888888@080888888088```>>>""" qqqppp"""yyy   """     yyyȜȜȜppp``> qy P2@@ng@ @@@@@@@@@ @@@@@ pNpp888ppp888pNpp888`P```(8(888888@ 888888<<<'''$$$$$$>>><<<P2fb.dat@@@@@@@@@@@@@ @@@@ @ C888888Î888888C888888`P(8(888888@(( PP@88PP@88``@``@8@8@8888<<<"""ppp""" < < < """ aaa yyy0 80 373 57:\#?; 0l6l6l6qG}? @??? 7ͳ 0l6l6l6qG} s`#s````TpppxxxP`?``pppPxxxUU1s;#33?7S@72L39C?????````x`Y>|>|?|?|}??_@@??'?'7?'?```x`Y>|>|>|>|} cg̟?Md{osos7g??O???????????*A>Ѐ>A? `?_/??0????*A>Ѐ>A0 0 880 0 777377S777:\#;##@__p 0 0 0 0l6l6l6qG}?_@?0 0 5_0  0 0 0 0l6l6l6qG}  3S\#3````Wu pxP?_@??  `` pPxUU0 0 883373?4,7S7773ͳ3;#;w?????``A>|``?|@?? _??????????``A>|``>|A csg̘3Md `O `o `w܈#@??????????????*A>Ѐ>A? ` ` ` `?@? ?//?????*A>Ѐ>A8|||8??  2@7 L3?``UUUU8|||9??@@?;``UUUU88 xsΏww1 u@8#?>|{ Pxx 0 0 0l6l6l6qG}? @??>| ?Sx 0 0 0l6l6l6qG}0 80 33?777s`;##P Pp 0 0 0 0l6l6l6qG}??`? (5\_ 7p 0 0 0l6l6l6qG}   &?0 0PA>00@A  ?  0 0PA>00>@A0 0 99337377S777332L=C9???````Y>|>|?|?|}?_?@?'?'7??```Y>|>|>|>|}    ##G̟gmlsoosoosn7g7??O????????????W>.>>A `_ @?/??W>.>>A0 0 8833777377S777:\#;#;#__ 0 0l6`l6q?_@??0 0 7_2Lp 0 0l6`l6q #?  @\#sp@__````````TpxxxP?? @@?p```````TpxxxP0 0 ;;777377S7773ͳ3?#;w????`xA>|&d&d>|>|>|@?_????`xA>|&d&d>|>|>|AOOo|n7973̙3# ````WupxxxP?`?   ````WupxxxP@jyhB1#69p7907903 c` @`````T|||||@?p?0?0? `   _`````T|||||@0 83773 5@33:\#9{@ @p 0 0 0l6l6l6qG}? @@? @;ۻ@?˓ʓ7ͳ?p 0 0l6l6l6qG} ``88??  xx||\\~~?~F@}@ppp 0 0 0 0 0 0l6`l6`}} ??  pp``@@``eb^ppp 0 0 0 0 0 0l6`l6`}} ``00  pp~~>>F@@pppl0`0l`000066}} `` 8 8  xx||\\~ ~ ?~@@}@ppp 6 6  }  }l`l`   pppp00^pppl0`0l`000066}}   pp``@@``ebXppp 6 6  }  }l`l`@x x88p|p|@@eb@g`rrrx=h7ph5z>~>~>z>@`  p0p```````pp00p p @ ```````7`37`3Uww]www`  ?`?>>FpppN8N8@N8????|^|~|~|^8x8x8x8x8?????__>}@~F@ppp 0 0 0 0 0 0l6`l6`}} ?????ggGGggb^eppp 0 0 0 0 0 0l6`l6`}}0????@F@pppl`0l0`000066}} 8??__>}@~@@ppp6  6 }   }l`l` ??ww77^pppl`0l0`000066}} ??ggGGggbXeppp6  6 }   }l`l```8>>wwGGb@e`grrrp h7x?h5z>~>~>z>``` p0p```````p0p @ ```````7`37`3Uww]www` ?|`?|??Fppp@N8N8N8????|^|~|~|^8x8x8x8xGFA-BASIC3@Dpqt@00000SCRNPHYSBASETHENTAXYFRAMEX1Y1SPCXCYCX1CY1TTSCROLLOFFFPPXPYXX1SCREENERRORGLGOALDOWNENDIFBCOUNTKOCJKTXCELPLAYQCOUNTTTTTMRXRYMMRPPASSCPGMMMFLDREFXREFYDXDYZTMDNQTRYTGGAINSCR1SCR2YARDLINESCRHALFOFFENSEGAMEOFFNESEDSHALFTIMECSFRAMEBKICKPUNTTACKLEYESINCENDGAMEDEFDEF1BXBYSTEPSTENDPLAY FIELDGOALGOODX2Y2X3X4 FILEDGOALFGFDPFDP1TDBIGPLAYS TEAMCOLORSG1G2G3TKLWAVEFUMFUM1FUM2INTBADCHARGEDEFENSEZTDIFFDIFF1ATRSAFETY POSSESSIONINT2 PPOSSESSIONCEL2XYZTYPERSISTPLAYMOREBLOBPATHPICPATHFILENAMETEMPREDONEBTEAMRTEAMTEAMSTWOBLUEBLANKTHREEPLAYERSCREENSCREEN1SCREEN2ODDNAMESHADOWZIPREDHOTBLUEHOTKEYKWITZPXPYPBXBYBPPPRPBGRIDP9P4PPPPALETTETDCHARGEVICTORYREDDEFBLUEOFFPLAYBOOKSITZLRRRBBBANDCOLRSCREENREDBLUEFIELDBALLBCELDIAGRAMREFTACKLERTEAMBTEAMKOBLUEKOBALLKICKERCROWDKOREDNAMEREDHOTBLUEHOTSPIKERBANDSPALETTEINSTARTCHANGEPOSSESSION BLUERUNNER REDRUNNERPICPICLOADSETUPMOVERJESSEENDSELCTTEAMSCHASERSCROLLMOVER2TACKLEFIELDFILEDPLAYERSKICKOFFTEXTMAINGAMEREDBALLCASEEXITTDARRAYPBALLSLOOPPBLUECELS BLUECELLSPASSSSSELECT SELECTPLAYCHOOSE CHOOSEPLAYLINEDIAGRAMSSTATSYARDLINEQUARTERENDIFFPUNTTITLEDISSOLVE PICDATALOADARRAYSUNOFFENSEDEFENSEHALFTIMEKICKOFF2 BLUEMOVERTACKLE2CHOOSE2BLUEBALLTEAMS2ARRAYS2PLAYERS2PASS2NEWGAMETD2 KICKTEAMSENDPLAYYESARRAY OLDROUTINE CREATEARRAYSBLUEKICKREDKICK REDKICKEAMSTITLEANIMATION TEAMCOLORSSTUPIDGETFORPAUSETBADVICTORYCHARGEENSELECTJUMPTEAMNAMELEVELSAFETY INTERCEPTIONCX1CX INTERCEPTION2SHUFFLESPIKEDELAYDANCESPIKERBANDPUT RESTORE_PAL RETAIN_PALSAVE_PALF================================================================ !F======================= Crunch Time Football =================== F=============== Copyright 1990 by Antic Publishing Inc. ======== 'F================================================================ 'Fp|F)FDFOFPF9FFF A߀F-F 8 FA.FF$F zM߀Fe FHFH!!Do you want to|play another game?!! Yes | Quit !{F >{߀FF$F:]Fcomplements retain_pal which is called in setup -FF"0%V#p߀ F  pF0`%߀F 8 Fp0`%߀F$F& *`݀pF0`F$F N\߀F 0\F$F ߀FF0F0F @ I߀F0IFG@ u߀F0uF 8 F0JF FFFFFF$F$ I ߀\ ݀F=FF `A߀FF@ <F Fl0 V F ߀F  `F@ `FFF$F 8 FFF$F< 6( ( A݀F0JF$F@ Z߀FF$FFF F0F$F6 4߀J݀M݀AF J߀FF F$F$F AMFF.FF0`V߀ F `p߀F0`߀F$F \߀F0\F$F ߀F0F0F0F @ I߀F0IFG@ k߀F0kF 8 :F0JF3FFFF4FF$F$ I ߀\ ݀F F0%V F %߀F  `F@ `FFF$F 8 FF$F AFF.@ 8( ( F0JF$F7FFFF vF0F$F@ j߀J݀M݀A݀F J߀FF F$F$F$ &A߀M݀FFF0 F 0 6F XF0#F F0#߀F0 F F0#߀F0 F  F0#߀F0 F HF0#F0 F F0#߀F"0 ߀FF0 F  F0#߀F"0߀ ߀F0 F bF0#߀F"0 ߀FF0 F  F0#߀F"0߀ ߀F0 F4F F0F@ ߘF0F$F 0F0F0F@ X߰F0F$F$0!H߀! F !!F! 8 F! 0FF 0 FF$F L F!! !F(!!!ݸ!F ,A߀F !!# F 8 FFA!!# F$F$F 0FF 0 FFFF  A߀F | F  F4 ߀ F0  F@  F4 ߀ F0  F$F F F"0   ߀FF@ v F"0 ߀  ߀F$F@  F  F4 ߀ F 0  F@  F4  F 0  F$F$F$F AF4߀ F0  F ( F  F"0 ߀  ߀F@  F"0   ߀FF$F$F$F  F0 F$@  ߰A݀F0 F@ : ߰AFFFF@ P F0 F@ x ߘF0 F$F !! FF( ! ! ! ݸ! F AF ! !F 8 FA ! ! F$F 0 FF 0 FFF================== FFF0F hs߀FT0F$F FT߀#ߠ !yߠ! F"!yݠ! F FFF0F0F Hs߀FT0F0sF$F F0 F0F0F 0FF 0 FF0 V F0 Vߠ F 0 FF 0 FF 4 FF 4 FF ! ! F!! FDFT GߠH F4݀!ݠ! F"!ݠ! F `FLF FF!! F0F0F0FF0F0JFF F 6 F  F2 7߀#9߀9݀ F(========== choose play ============ t!!Fd!2!!F F|d!2!!F p!!!FNT|!F! LAST PLAYFp!!!FN 9߀FH Tt!Z! OF THE HALFF@ 9߀FH Tt!Z! OF THE GAMEF$Fp!!!FETn!x! Field GoalFp!!!FETn!! Go for ItFF 0]FG0^FG0\FF0 F `FF ߀F 0]]߀F@ ߀F 0]]߀F$F  ]F0]F@ 4]F0]F$F $ ߀݀F=p!!!Fn 0 ^F FTn!x! Field GoalF FTn!! Go for ItFF4F p!!!F 0 "]F jFTn!x! Field GoalF0IF0\FG FTn!! Go for ItFF0IF0\F4F 0^]FF$F v F$F$ 8߀\ ݀F(========== choose play ============ t!!Fd!2!!F F|d!2!!F p!!!FN Tt!F! FOURTH DOWNF=p!!!FN Tt!Z! CHOOSE PLAYF=p!!!FTn!n!PuntFp!!!FnTn!x! Field GoalFTn!! Go for ItFF 0]FG0^FG0IF0\FF0 F `FF ߀F 0]]߀F@ F߀F 0]]߀F$F l]F0]F@ ]߀Fo0]F$F $ ߀݀F=p!!!Fn 0 ^F FTn!n!PuntF FFTn!x! Field GoalF tFTn!! Go for ItFF4F p!!!F 0 ]F FTn!n!PuntF0IFG0\F 4FTn!x! Field GoalF0IF0\FG rFTn!! Go for ItFF0IF0\F4F 0^]FF$F  F$F I\FT!!!Fot!!F FL/GF" /! ! /! !/ F | /F0/F/ F|!!d!KF F0 F l F FL| /! ! /! ! /! ! /! F 0 FF "FF0/F DF0/F fF0/F F0/F F0/F F0/F  F0/F  F0/F4F FL| /! ! /! ! /! ! /! F$F 0$/FF `F | F0%V߀ F %߀FF0$$߀F$F$FFF F0F0F0FF0&F0F0F0F 0FF 0 FF0F0 F 4 FF I\F!! FF 8 FIAF$FFFTZGH(F!F! F  F!TnG߾H(F!F!!! F&F!B!U!]! FTZGHF!d! F .F!TFGHF!x! F .F!&F!x!U!! F /I߀F!!! F!! F@ /\߀F!! F!! F$FF / FDF 0I߀FTGߌHF"!߂! F `FLF 04F!! F@ 1 \߀FTGߴHF,"!߂! F `FLF 0F!! F$F LSGFUTZGHF(!ߌS! F(!S݀! F 1^FTFGHF(!ߌS݀! F(!S݀! F 1F | 1@SF  2vI߀F!! F@ 2\߀F!! F$F 2\߀F!! F$FDFTGߠH F4!ߠ! F"!ߠ! F `FLF 3F  9\߀F&============ field goal ======== FF"0%%T ߠ F 4%F0VF 8 4$Fi0VF$Fp!!! FT!!GFT0!!GF FT!!!F=ls!!!F?l!!!F?l!#!!F?ls!!!F?T!!!FDF  5V߀F0%%T  F 0%ݠFF0W%F0 F0XF@ 7VF0 F"0%%T ߀ F 0 6 %F 6HF0F0WF0XF 6F0F0WF0XF 6F0F0WF0XF 7F0F0WF0XF4F$FLG2FL# #W ! # X ! F>#W ! # X ! F `F 7 FLF$F | 7.Fp!!! F 8vV߀F Tp!P! Kick is GoodF0<<F0FKF 8 8FV$Td!P!Kick is No GoodFJF$F0AF0FG PAUSE 100 t!!F !!?!!FTGߠHFF.!݀!?!݀F!!F 94FZ ߠ JF ! ! FF0 F( ! ! ! ݸ! F ! ! FF  =F!! F$F. >>( ( ߠF0JF$F `FF  =HF( ?l ߟ ݉JF ! ! FF0 F( ! ! ! ݸ! F ! ! FF  ? F!! F$F. ?P( ( ߠF0JF$F `FF  >ZF0F ! ! F! 4 FF0`FF====================  Fp!!! FLG F8T!ߠ! FF8T0!ߠ! FF | ?F$ @ߴA݀F.0## F$F AߠAF0ߠF$FFFFF AVA߀F0DF 8 ArF0D߀F$F 4 FF 4 FF0ߠDF0߀F F BA߀FF F0 ߀F BFF0F$F0F0 F0FLGF 4 FF `FF BF50F$F | BdFG0F0 F0F 8 CFA 0#ߴ ߀F CDߠF0F$F0 F0F0FLGF 4  FF `FF CF50F$F | C|FG0F0F0 F$FF FF0%Vߠ F( F%߀L9߀F: F#A݀i݀ #Aj߀ Fp!!! F XFT!!FUMBLEF!! F! ! ! F!$ Eߑ ݑF0 FF$F EFF$F RLF! RA߀F0::#> FF 8 RFA0::#> FF$F$F088߀F S,:߀F008F0:F$F 0>FF S~t߀F0tF08F0:F$F@ T:߀I݀i݀j݀F0IF0F08F0:F Ti߀F0iF@ T4j߀F0jF$F$F T\߀F08F0:F0_F0FG0 F$F( VP8ߠ:A߀FDFt!!FP!F!!sFap!!!F XF$TT!P! Play is ShortF&TT!Z! Of a First DownF(TT!n!Opponent Takes OverF=JFJFF4SF UqFF0MF$F U M߀F LF08F0:F0AF0F$F077߀F#F W 7F4SF VqFF0MF 8 WFN%F099߀F07F W9ߠF#F$F$F$F WvMFF4SF WPqFF0MF$F W& M߀F $F0LFF%FDFF W Fp!!! F*$ Z9߀9FFFT!!End ofF XL9߀F"Tl!! First QuarterF) 8 XxF9"Tl!! Third QuarterF)$Ft!!Fd!!?!cFa$============================== the wave =$============================== LGF?6#݀ !d! F | YF#FLgGF!d! F ` FLGF6# ߀!d!#߀  F ` F6#݀ !d! F | YF ` F0!d!( F | Y|gFG `(FLF@ Z9߀FFF/F0F0AF@ [9߀Ft0MFGFp!!! FJT|!d! Game OverFC ` Fp!!! FJT|!d! Game OverFC ` F Z F)F$FF#F0put data in setup XFt!!F!!,!F, F|!!,!F,p!!!F,T!!QuarterFTK!!TimeFT!!FT!!OpponentFT!!DownFT!! YardsFFT!!To GoFs DEFTEXT 2,0,0,4 FT+!!79F0 ]T7ߠF TN!!77F 8 ]tFNTH!!77F0$FTT!!:00F0T!!7check for necessities: low rez, data files in same folder 4FF bV FR!1Crunch Time Football runs|in low resolution only.!! Quit !|F F$F bTfb2.dat FoZ!8File FB2.DAT must be in the|same folder as FOOTBALL.GFA.!! Quit !|F F$F cTfb3.dat FmZ!8File FB3.DAT must be in the|same folder as FOOTBALL.GFA.!! Quit !|F F$F cTfb4.dat FmZ!8File FB4.DAT must be in the|same folder as FOOTBALL.GFA.!! Quit !|F F$F6^Fcomplements restore_pal called in main loop a LGF!LGF,FAH !9F, | cF | cF0,4,0,0,0,0,7,0,0,0,7,0 0,0,7,0,7,7,7,7,0,7,5,5 7,7,7,4,4,4,6,6,7,5,0,5 7,6,0,0,0,5,0,7,5,7,3,0  LGFFJ! ݀! ! ߀! ! ߀! F | dF (Fp!!!F 4T!!! Loading Graphics ... Please WaitFp!!!F4T !! Loading Graphics ... Please WaitF LGFLGFFH !9F | eF | eF 1,4,7,11,15,16 5,8,9,13,16,18 5,3,8,12,17,18 3,4,8,16,11,18 10,8,1,18,12,17 8,9,5,12,16,19 1,9,4,13,15,16 9,1,4,20,15,11  1,4,7,11,16,19 5,8,9,18,13,16 5,3,8,17,12,18 3,4,8,15,11,18 10,8,1,17,12,18 8,9,5,18,12,16 1,9,4,13,16,18 9,1,4,16,11,20  LGF1LGF1F!H!9F1 | gF | grFrem data for red defense 10,12,16,10,12,14 10,12,16,10,14,16 10,12,16 1 LG F LGF F7H!9F | h<F | h(Frem data for blue offense "2,4,6,6,4,9,5,6,9,1,7,4,1,4,8 "3,4,7,3,5,6,3,8,9,4,7,3,6,4,8  LGFLGFFH!9F | hF | hFrem data for playbook 3,2,8,6,4,6,8,9,7,3 1,6,7,5,10,1,2,3,4,8 2,10,1,9,5 1 H ! FLG FFH!9F | iF01,3,5,6,8,10,12,13,15,15,13,13,12,12,10,10 13,12,12,10,12,12,15,15 13,13,13,13,13,13,13,13  H ! FLG FFH!9F | jrF 6,8,10,13,13,10,13,13,13,13  LGF$H!9߀F( | jF LGF1 !FH !9FH !9F | kF<20,25,120,25,220,25,20,85,220,85,20,145,120,145,220,145 *F +FT6F !!!! F&!! !! FCFF F------------- SAVE ORIGINAL COLOR PALETTE ----------------------- ^F H! F XGFE!F߀!FO!FO !!F!߀!FOXF P!9@# F P!9@# F P!9@# F lFF ]F@--------------------- RESTORES PALETTE ------------------- - Dimensions: Spalette%(16,3)  XGFt!F߀!Fe!Fe !!F!߀!! F!߀!! F!!! FXF mFF (F!!!F!FF XFp!!! FJ$TQ!!Poppy and JesseFnp!!! FJ$TP!!Poppy and JesseFnp!!!FJT{!3!presentFdp!!!FJTz!2!presentFdp! !! F8 TA![! Crunch TimeFFp! !! F8 T@!Z! Crunch TimeFFp! !! F8TP!y!FootballFp! !! F8TO!x!FootballFp!!!FJ<T!!(Copyright 1990 by Antic Publishing, Inc.Fp!!!FJ<T!!(Copyright 1990 by Antic Publishing, Inc.FFCFt!!Fs !! !F p!!!F 4T !!Press Fire Button to Start GameF=p!!!F4T!!Press Fire Button to Start GameF=Fp! !! F8 TA![! Crunch TimeFFp! !! F8 T@!Z! Crunch TimeFFp! !! F8TO!y!FootballFp! !! F8TP!x!FootballF rd FFDFLGFFJ! ݀! ! ߀! ! ߀! F | sFL/G F,/!U! F | s/Ft!!F!VF!_Ft!!F!eFt!!F5!VF5!_Ft!!F5!eFt !!FS!VFS!eFt!!FS!_Ft !!Fq!VFq!_Ft!!Fq!eFt!!F!VF!_Ft!!F!eFt!!F!VF!eFt!!F!_Ft!!F!VF!eFt!!F!_Ft !!F!VF!eFt!!F!_Ft !!F!VF!eFt!!F!_Ft!!F%!VF%!_Ft!!F%!eF p!!! FT!!1Fp!!!F,T6!2!Select your team colors:F 0/F FB|/!P!ߌ/!nF F0 F zt F FB|/!P!ߌ/!nF 0 yFF yFV0//߀F yFV0//߀F4F z/݀F0/F@ z /ߠF!0/F$F FB|/!P!ߌ/!nF$F `F y F 0 z/F0 zF0cF,0dF,0eF, {2F0cF,0dF,0eF, {xF0cF,0dF,0eF, {F0cF,0dF,0eF, |F0cF,0dF,0eF, |JF0cF,0dF,0eF, |F0cF,0dF,0eF, |F0cF,0dF,0eF, }F0cF,0dF,0eF, }b F0cF,0dF,0eF,4F 0cc߀F0dd߀F0ee߀F0 ! c! ! c! ! c! Fhuman helmet 0 ! d! ! d! ! d! Fhuman shirt 0! e! ! e! ! e! Fhuman pants  0%%T  F ~%F !!!F !!!F1@ "%߀FF !!!F !!!F@ t%߀F !!!F !!!F$FFOFFFp!!! FrT!!2Fp!!!Fr2T,!2!Enter Your Name or Team Name:FTx!`!________F!!!Fl! Fd!F!!!FlFPFFF .========== choose difficulty ============ p!!! F T!!3Fp!!!F ,TD!2!Select Difficulty LevelFNp!!!Ff Td!d! High SchoolFTd!x!ProFeTd!! Super BowlFp!!!FlTd!n!CollegeFo 0pFS0qFSF0 F `FF x߀F 0pp߀F@ ߀F 0pp߀F$F pF0pF@ pߠFi0pF$F $ <߀݀F=p!!!Fn 0 LqF |F Td!d! High SchoolF FTd!n!CollegeFo FTd!x!ProFe FTd!! Super BowlF4F p!!!F 0 4pF vF Td!d! High SchoolF0pF FTd!n!CollegeFo0pF FTd!x!ProFe0pF .FTd!! Super BowlF0pF4F 0qpFF$F 2 F F9F0kF0uF0iF0jF0MF0{F07F:09F:0110,160,2,100,150,5,90,140,10,80,130,5,70,120,6,60,110,5 ,650,100,6,40,90,5,40,80,6,40,70,5,40,70,6,40,70,5 0rem l half run right 4@110,160,2,120,160,7,130,160,8,140,160,7,150,160,8,160,160,7 @170,160,8,180,160,7,190,160,8,200,160,7,210,160,8,220,160,7 rem i-back run left D150,160,2,150,170,10,140,180,9,130,180,10,120,180,9,110,180,10 >100,180,9,90,180,10,80,180,9,70,180,10,60,180,9,50,180,10 rem rt half block right 1 @180,160,2,190,160,7,200,160,8,210,160,7,220,160,8,230,160,7 @240,150,6,240,140,5,240,130,6,240,120,5,240,110,6,240,100,5 rem rt half block right 2 @190,160,2,200,150,6,210,140,5,220,130,6,230,120,5,240,110,6 <240,100,5,240,98,6,240,96,5,240,94,6,240,92,5,240,90,6 5rem rt flanker slant in >240,120,2,240,110,5,240,100,6,240,90,5,240,80,6,230,70,5 ,:220,60,6,210,50,5,200,40,6,190,30,5,180,20,6,170,10,5 rem rt flanker out >240,120,2,240,110,5,240,100,6,240,90,5,240,80,6,230,70,5 &:240,60,6,245,55,5,250,50,7,255,45,8,260,40,7,265,35,8  (============ Defense ============= ,rem l corner wide out 670,40,1,65,40,9,60,40,10,55,40,9,50,40,10,45,40,9 640,40,10,40,50,3,40,50,4,40,55,3,40,60,4,40,65,3 rem l corner rush 470,40,1,70,50,3,70,60,4,70,70,3,70,80,4,70,90,3 <70,100,4,70,110,3,80,120,4,90,120,7,100,120,8,110,120,7 rem l corner contain ,470,40,1,65,45,3,60,50,4,55,55,3,50,60,4,45,65,3 440,70,4,35,70,3,30,80,4,25,85,3,20,90,4,15,95,3 rem l safety rush 8130,30,1,120,35,3,110,40,4,100,45,3,90,50,4,80,55,3 470,60,4,70,65,3,70,70,4,70,75,3,70,80,4,70,85,3 rem l safety deep <130,30,1,130,20,3,125,20,10,120,20,9,115,20,10,110,20,9 8105,20,10,100,20,9,95,20,10,90,20,5,80,20,6,75,10,5 rem rt safety slant :170,30,1,175,35,3,180,40,4,185,45,3,185,45,4,185,45,7 :180,40,6,175,35,5,170,30,6,165,25,5,160,20,6,155,15,5 rem rt safety sideline ,:170,30,1,175,35,3,180,40,4,185,45,3,190,45,4,190,45,7 :195,45,8,200,45,7,205,45,8,210,45,7,215,45,8,220,45,7 rem rt corner pass/run 0:230,40,1,230,35,3,230,30,4,230,25,3,230,20,4,230,30,3 :230,40,4,230,50,3,230,60,4,230,70,3,230,80,4,230,90,3 rem rt corner contain :230,40,1,240,40,7,250,40,8,260,40,7,270,45,4,280,50,3 :285,55,4,285,60,3,285,65,4,285,70,3,285,75,4,285,80,3 rem rt corner slant :230,40,1,230,35,3,230,30,4,230,25,3,230,20,4,230,20,9 >225,20,10,220,20,9,215,20,10,210,20,9,205,20,10,200,20,9 , F6FL%GF,LG F!!FH%!!9FH%!!9FH%!!9F | F! | %F! .================= Offense =============== rem qb pass right :150,40,1,150,40,9,150,40,5,145,35,6,140,30,5,135,25,6 <130,20,5,125,15,6,120,10,10,115,10,9,110,10,10,105,10,9 rem qb run left :150,40,1,160,35,5,170,30,6,180,30,7,190,30,8,200,30,7 :210,35,8,220,40,7,230,45,8,240,50,7,250,55,8,260,60,7 rem qb pass left ,:150,40,1,150,40,9,150,40,5,155,35,6,160,30,5,165,25,6 :170,20,5,175,15,6,180,10,8,185,10,7,190,10,8,195,10,7 rem rt half run right :120,10,1,115,15,9,110,20,10,100,25,9,90,30,10,80,35,9 870,40,10,60,45,9,50,50,10,40,55,9,30,60,10,20,65,3 9rem rt half off tackle 0:120,10,1,115,15,3,110,20,4,110,25,3,110,30,4,110,35,3 :110,40,4,110,45,3,110,50,4,110,55,3,110,60,4,110,65,3 rem left half sweep right <190,10,1,180,10,9,165,10,10,150,10,9,135,10,10,120,10,9 8105,10,10,90,14,9,80,18,10,70,22,9,60,26,10,50,30,3 rem left half pass =:190,10,1,200,15,3,210,20,4,220,25,3,230,30,4,240,35,3 :250,40,4,260,45,3,260,55,4,260,65,3,260,75,4,260,85,3 rem rt flanker fly 6660,60,1,60,70,3,60,80,4,60,90,3,60,100,4,60,110,3 :60,120,4,60,130,3,60,140,4,60,150,3,60,160,4,60,170,3 rem l flanker out :240,60,1,240,65,3,240,70,4,240,75,3,240,80,4,245,85,3 >250,90,4,255,95,3,260,100,4,270,100,7,280,100,8,290,100,7 6===================== Defense ================== 7rem left corner rush ,:60,140,2,60,135,5,60,130,6,60,125,5,60,120,6,60,115,5 665,110,6,65,100,5,65,90,6,70,80,5,70,70,6,70,60,5 rem left corner fly :60,140,2,60,145,5,60,150,6,65,145,5,70,140,6,75,135,5 :80,130,6,80,130,7,80,130,3,80,140,4,80,150,3,80,160,4 rem rt corner rush ,@240,140,2,240,135,5,240,130,6,240,125,5,240,120,6,240,115,5 <235,110,6,235,100,5,235,90,6,230,80,5,230,70,6,230,60,5 rem rt corner out @240,140,2,240,145,5,240,150,6,240,155,5,240,160,6,240,155,5 @245,150,6,250,145,7,260,140,8,265,135,7,275,130,8,280,125,7 rem rt half rush c@190,160,2,190,155,5,190,150,6,195,145,5,195,140,6,195,135,5 @200,130,6,200,125,5,200,120,6,205,115,5,205,110,6,205,105,5 rem rt half out @190,160,2,195,155,5,200,150,6,205,145,5,210,140,6,215,135,5 @225,130,7,235,125,8,245,125,7,255,125,8,265,125,7,275,125,8 rem left half rush ,@110,160,2,110,155,5,110,150,6,105,145,5,105,140,6,105,135,5 >100,130,6,100,125,5,100,120,6,95,115,5,95,110,6,95,105,5 ,rem left half fly >110,160,2,105,155,5,100,150,6,95,145,5,90,140,10,85,140,9 :80,145,4,80,150,3,80,155,4,80,160,3,80,165,4,80,170,3 F==================== 0FFF0F0F \s߀FT0F$F FT2GHF!! F ߂F0F$F F!!! FT߀#ߠ !yߠ! F"!yݠ! F zFFF0F s߀FT0F0sF$F F0 F0V F0V F 0FF 0 FF0 F0 F 0 FF 0 FF 4 FF 4 FF ! ! F!! FDFT GߠH F>!ߴݠ! F,!ߴݠ! F `FLF F 0FF0F0F0JF0FF3FF  F  F@ V7߀#9߀9݀ F0\F$F *8 ߀\F ,========== choose defense ============ ,t!!Fd!2!!F F|d!2!!F p!!!FJT!F!SELECTFT!P!DEFENSEFp!!!FTn!d! Basic 4-3Fnp!!!Fn Tn!n! Run PreventFF Tn!x! Pass PreventFTn!!BlitzFrTn!! Goal LineFn 0NF0OFF0 F `FF ߀F 0NN߀F@ ߀F 0NN߀F$F NF0NF@ NF0NF$F $ ߀݀F=p!!!Fn 0 xOF FTn!d! Basic 4-3Fn F Tn!n! Run PreventFF F Tn!x! Pass PreventF 0FTn!!BlitzFr ^FTn!! Goal LineFn4F p!!!F 0 NF FTn!d! Basic 4-3Fn F Tn!n! Run PreventFF "F Tn!x! Pass PreventF LFTn!!BlitzFr zFTn!! Goal LineFn4F 0ONFF$F ^ F SELECT red defense 0N! F0N! F0N! F CASE 1 dLG F!L'GF H!!'9!!' F8 H!!'9!!' F8 H!!'9!!' F8 | ('F! | F! 0 NFe F,0F10F1 F,0F0F PF0F0F F0F0F F0F0F4F 0%Vߠ F 0 NF% F10$Vߠ F R!!F0$N߀!% F  F ߠF 0$!% F 8 F0$!% F $F4F SELECT blue offense 0$! F0$! F0$! FLG F!L'GF H!!'9!!' F8 H!!'9!!' F8 H!!'9!!' F8 | 0'F! | F! 0 $FT" !!! F0-F. &!!!!! F0-F4F@ 8߀F  hF*0IF 8 F0\F$F$F0%V߀ FN %߀N#$$݀ u߀F0uF$FF4FF F0F0F0FF0&F0F 0FF 0 FF 0 |$F" !!! F=0PF0QF !! F0PF0QFF <!FF0PF0QF pF0PF0QF4F0 ! ! F0 ! ! F 0 FF 0 FF0F0 F4 F(!!!ݸ!F 6I\F5FF 8 HFI@FF$FF5FF!! F.!!!! FP!Q! F:P!Q!P!Qݸ! FTZGHF!F! F F! LGF)2!! !!! ! F2!! !!! ! F | FF 4  F 4 FFTZGH(F!d! F F!TnG߾H(F!n! F 4F!F!! F `F!! F `F \ FDFTGHF "!߂! F `F FLF!(! FLSGFTZGH(F(!߂S! F(!S݀! F \FTnG߾H(F(!߂S݀! F(!S݀! F F | >SF d$F!(! FDFTGߠHF*!݌! F*!݌! F `FLF Fn!Q! Fn!f! F!F! F!n! F$@ <$߀$ݠF!(! FDFTGߠHF*!݌! F*!݌! F `FLF F@ $ߐF!(! FDFTGߠHF*!݌! F*!݌! F `FLF F$FF7F1 jF0 F(&!! !&!! ! F5 &߀FV&!݀! !&!݀! !&!! F 8 6F&V&!݀! !&!݀! !&!! F$F(&!! !&!! ! F!V&!݀! !&!݀! !&!! F&F! &߀F0&F0߀F$F0 !! F0 !! F$ d߰-݀F8F$F 8 F0 FpFh `F$FF8F4! ! !! ! ! F:! ! !! ! ! F0)! ! F0*! ! F0,! ! F0%V F %߀F 0))ߠF0**ߠF0LF0JF0F$F .u߀F$ 0)FF 0*FF0LF$FDFL+G F ìLFN +##)  ! +##* ߠ ! F 8 FL\#ߠ+݀  +##) ߠ ! +##* ߠ ! F$FB +##) ߠ ! +##* ߠ ! F `F#LF+ | P+F ĤL߀FF)!*! F@ u ߀F$ 0 )FF 0 *FF 0 FF 0 FF$F u߀F$UF$F0-F0FTFUF0AF0JF0F0tFDF F,߀!! !Fp!!! F T@!! INTERCEPTIONFLGFLG FT"! !!!F | F | F!FLFF@FTFGHF!d! F ƘF!!! F&F!d!U!! FTZGHF!F! F F!TFGHF!2! F XF!&F!.!U!I! F I߀F!(! F! ! F@ 4\߀F! ! F!! F$FF @ FDFTGߌHF"!߂! F `FLF xF \߀F!! F 8 F! ! F$F LSGFUTZGHF(!ߌS݀! F(!S! F LFTFGHF(!S݀! F(!S݀! F F | .SF ^I߀F! ! F@ ʨ\߀F!! F!! F$FDFTG߾H F*!! F!! F `FLF F А\߀F&========= field goal ============ FF"0%%T ߠ F ˺%F0VF 8 Fi0VF$Fp!!! FT!!GFT0!!GF FT!!!F=ls!!!F=l!!!F=l!!!F=ls!!!F=T!!!FDF  dV߀F0%%T  F 0%ݠFF0W%F0 F0XF@ VF0 F"0%%T ߀ F 0 ͪ%F F0F0WF0XF 6F0F0WF0XF |F0F0WF0XF F0F0WF0XFF4F$FLG2FL# #W ! # X ! F>#W ! # X ! F `F ϔ FLF$F | Fp!!! F 0V߀F Tp!P! Kick is GoodFJFJF0==F0F 8 ^FV$Td!P!Kick is No GoodF$F0AF0FF0F$FF=================== KF `FLGFF0! F"! !!!F | FLG F0! F0F ӨF0F0F$F! !!!F | HF `F!FFJFLG F,! !!!F | FLG F,! !!!F | dFLG F,! !!!F | ԰F!FFLF `FLG F0! F bF10F 8 ՒF0F0F$F! !!!F | (Fp!!! F$!!!!FTh!d!CHARGE!F $! !!! F!F `FF/FP!!!Ft!!F!!?!cF!#Fp!!! F XFDT!F!HalftimeF XFLGF=6#݀ !d! F | 2Ft!!F!!?!Ft!!F!!?!FNFTGHF߀!! F!! FNF!! F ߠ!! FNF߈!! F !! FNF `F!! F ߀!! FNFߠ!! F !! FNF!! F !! FNF `F F!LGF=6#݀ !d! F | ٶF[FTGHFF&݀!! F"!! FNF&!! F,ݠ!! FNF&݈!! F,!! FNF `F!! F,݀!! FNF&ݠ!! F,!! FNF&!! F,!! FNF `F FGLGF=6#݀ !d! F | ^FF ܦ F)FFNF0%V F0%! F n߀F@#%݀ !d!%߀ FH%!9߀F(@ ߀F6#%݀ !d!% FH%!9߀F($FFVFt!!FLGF!! FXF!! FXF | FG!! FXFa!! FDF PFI,ߠ!݀!!F0ߠF `FLF,ߠ!݀!!F0ߠF `FLF  ސFFWFt!!F ! ! FXF XF  ! ! FDF v FI, ߠ! ݀!!F0 ߠF `FLF, ߠ! ݀!!F0 ߠF `FLF  ߶F FXF ` FF[F0xFTtG߲HFTG߀HF!! F `F F0x! F0xx߀F0F F0F0F$F! !!!FTGHF!! F `F F0x! F0xx߀F0F zF0F0F$F! !!!F x߀F0xF$F F!FF. #f.. DXF $fRESOURCE 0f:RSC_TO_S 6f\. $f.. "fDXF DOC BvDD[ This file is an excerpt from the AutoCAD Release 10 Reference Manual. Copyright (C) 1988 Autodesk, Inc. All rights reserved. ] Appendix C Drawing Interchange and File Formats AutoCAD can be used by itself as a complete drawing editor. In some appli- cations, however, other programs must examine drawings created by AutoCAD or generate drawings to be viewed, modified, or plotted with AutoCAD. For example, if you've made an architectural drawing with AutoCAD, using INSERTed parts to represent windows, doors, and so on, you can process the drawing file and produce a bill of materials of all the items used in the drawing, or even make energy use calculations based on the area and the number and type of windows used. Another possible application is to use AutoCAD to describe structures that are then sent to a large computer for finite element structural analysis. You can compute stresses and displace- ments and send back information to display the deformed structure as an AutoCAD drawing. Since the AutoCAD drawing database (.dwg file) is written in a very compact format that changes significantly from time to time as new features are added, we do not document its format and do not recommend that you attempt to write programs to read it directly. To assist in interchanging drawings between AutoCAD and other programs, a "Drawing Interchange" file format (DXF(tm)) has been defined. All implementations of AutoCAD accept this format and are able to convert it to and from their internal drawing file representation. AutoCAD also supports the Initial Graphics Exchange Standard (IGES) file format. The information comprising an AutoCAD drawing can be written out in IGES format, and IGES files can be read and converted to AutoCAD's internal format. C.1 ASCII Drawing Interchange (DXF) Files This section describes AutoCAD's DXF (drawing interchange) file format and the commands provided to read and write these files. DXF files are stan- dard ASCII text files. They can easily be translated to the formats of other CAD systems, or submitted to other programs for specialized analysis. C.1.1 DXFOUT Command - Writing a DXF File You can generate a drawing interchange file from an existing drawing by means of the Drawing Editor's DXFOUT command. The command format is: Command: DXFOUT File name : (name or RETURN) The default name for the output file is the same as that of the current drawing, but with a file type of ".dxf". If you specify an explicit file name, do not include a file type; ".dxf" is assumed. If a file with the same name already exists, it is deleted. Next, DXFOUT asks what precision 1 AutoCAD Reference Manual you want for floating-point numbers and permits output of a partial DXF file containing only selected objects. Enter decimal places of accuracy (0 to 16)/Entities/Binary <6>: The "Binary" option is described later in this appendix. If you respond with "Entities" (or just "E"), DXFOUT will ask you to select the objects you want written to the DXF file. Only the objects you select will be included in the output file - symbol tables (including Block Definitions) will not be included. Once you've selected the desired objects, AutoCAD will prompt again for the numeric precision: Enter decimal places of accuracy (0 to 16)/Binary <6>: C.1.2 DXFIN Command - Loading a DXF File A drawing interchange file can be converted into an AutoCAD drawing by means of the DXFIN command. First enter the Drawing Editor using the "Create new drawing" task from the Main Menu. Then issue the DXFIN com- mand. Command: DXFIN File name: (name) Enter the name of the drawing interchange file to be loaded. Full DXFIN To load a complete DXF file, you must use DXFIN in an empty drawing, before any entities have been drawn and before any additional Block definitions, layers, linetypes, text styles, named views, named coordinate systems, or named viewport configurations have been created. (If your prototype draw- ing contains any such items, use Main Menu Task 1's "name=" technique to create a new drawing without a prototype.) If any errors are detected during the input, the new drawing is discarded. Otherwise, an automatic "ZOOM All" is performed to set the drawing extents. Partial DXFIN If the current drawing is not empty, DXFIN loads only the ENTITIES section of the DXF file, adding the entities found there to the current drawing. In this case, DXFIN displays the message: Not a new drawing -- only ENTITIES section will be input. If errors are detected during such partial DXF input, the drawing is returned to the state it was in before the DXFIN command. Otherwise, the newly added entities are drawn. 2 (C) Drawing Interchange and File Formats C.1.3 DXF File Format This section describes the format of a DXF file in detail. It contains a great deal of technical information that you need only if you're writing your own program to process DXF files. Otherwise, you can skip this sec- tion. It would probably be helpful to produce a DXF file from a small drawing, print it out, and refer to it occasionally while reading the information presented below. C.1.3.1 General File Structure A Drawing Interchange File is simply an ASCII text file with a file type of ".dxf" and specially-formatted text. The overall organization of a DXF file is as follows: 1. HEADER section - General information about the drawing is found in this section of the DXF file. Each parameter has a variable name and an associated value. 2. TABLES section -- This section contains definitions of named items. o Linetype (LTYPE) table o Layer table o Text style (STYLE) table o View table o User Coordinate System (UCS) table o Viewport configuration (VPORT) table o Drawing manager (DWGMGR) table (for future use) 3. BLOCKS section - This section contains Block Definition entities describing the entities comprising each Block in the drawing. 4. ENTITIES section - This section contains the drawing entities, including any Block References. 5. END OF FILE If you use DXFOUT's "Entities" option, the resulting DXF file will contain only the ENTITIES and END OF FILE sections, and the ENTITIES section will reflect only the objects you select for output. A DXF file is composed of a multiplicity of groups, each of which occupies two lines in the DXF file. The first line of a group is a group code, which is a positive nonzero integer output in FORTRAN "I3" format (that is, right justified and blank filled in a three character field). The second line of the group is the group value, in a format which depends on the type of the group as specified by the group code. 3 AutoCAD Reference Manual The specific assignment of group codes depends upon the item being described in the file. However, the type of the value this group supplies is derived from the group code in the following way: Group code range Following value 0 - 9 String 10 - 59 Floating-point 60 - 79 Integer 210 - 239 Floating-point 999 Comment (string) Thus a program can easily read the value following a group code without knowing the particular use of this group in an item in the file. The appearance of values in the DXF file is not affected by the setting of the UNITS command: coordinates are always represented as decimal (or possibly scientific notation if very large) numbers, and angles are always repre- sented in decimal degrees with zero degrees to the east of origin. Variables, table entries, and entities are described by a group that intro- duces the item, giving its type and/or name, followed by multiple groups that supply the values associated with the item. In addition, special groups are used for file separators such as markers for the beginning and end of sections, tables, and the file itself. Entities, table entries, and file separators are always introduced with a 0 group code that is followed by a name describing the item. C.1.3.2 Group Codes Group codes are used both to indicate the type of the value of the group, as explained above, and to indicate the general use of the group. The spe- cific function of the group code depends on the actual variable, table item, or entity description. This section indicates the general use of groups, noting as "(fixed)" any that always have the same function. Group code Value type 0 Identifies the start of an entity, table entry, or file separator. The text value that follows indicates which. 1 The primary text value for an entity. 2 A name; Attribute tag, Block name, etc. 3-4 Other textual or name values. 5 Entity handle expressed as a hexadecimal string. 6 Line type name (fixed). 7 Text style name (fixed). 8 Layer name (fixed). 9 Variable name identifier (used only in HEADER section of the DXF file). 10 Primary X coordinate (start point of a Line or Text entity, center of a Circle, etc.). 11-18 Other X coordinates. continued ... 4 (C) Drawing Interchange and File Formats Group code Value type 20 Primary Y coordinate. 2n values always correspond to 1n values and immediately follow them in the file. 21-28 Other Y coordinates. 30 Primary Z coordinate. 3n values always correspond to 1n and 2n values and immediately follow them in the file. 31-37 Other Z coordinates. 38 This entity's elevation if nonzero (fixed). Output only if system variable FLATLAND is set to 1. 39 This entity's thickness if nonzero (fixed). 40-48 Floating-point values (text height, scale factors, etc.). 49 Repeated value - multiple 49 groups may appear in one entity for variable length tables (such as the dash lengths in the LTYPE table). A 7x group always appears before the first 49 group to specify the table length. 50-58 Angles. 62 Color number (fixed). 66 "Entities follow" flag (fixed). 70-78 Integer values, such as repeat counts, flag bits, or modes. 210, 220, 230 X, Y, and Z components of extrusion direction. 999 Comments C.1.4 Comments The 999 group code indicates that the following line is a comment string. DXFOUT does not currently include such groups in its output file, but DXFIN honors them and ignores the comments. Thus, you can use the 999 group to include comments in a DXF file you've edited. For example: 999 This is a comment. 999 This is another comment. C.1.5 File Sections The DXF file is subdivided into four sections. File separator groups are used to delimit these file sections. The following is an example of a void DXF file with only the section markers and table headers present. 0 (Begin HEADER section) SECTION 2 HEADER <<<
>>> 0 ENDSEC (End HEADER section) 0 (Begin TABLES section) SECTION 2 5 AutoCAD Reference Manual TABLES 0 TABLE 2 VPORT 70 (viewport table maximum item count) <<<>>> 0 ENDTAB 0 TABLE 2 LTYPE, LAYER, STYLE, VIEW, UCS, or DWGMGR 70 (Table maximum item count) <<<>>> 0 ENDTAB 0 ENDSEC (End TABLES section) 0 (Begin BLOCKS section) SECTION 2 BLOCKS <<<>>> 0 ENDSEC (End BLOCKS section) 0 (Begin ENTITIES section) SECTION 2 ENTITIES <<<>>> 0 ENDSEC (End ENTITIES section) 0 EOF (End of file) C.1.5.1 HEADER Section The HEADER section of the DXF file contains settings of variables associated with the drawing. These variables are set with various commands and are the type of information displayed by the STATUS command. Each variable is specified in the header section by a 9 group giving its name, followed by groups that supply its value. The header variables, the groups that follow, and their meanings are listed below. Although this list is very similar to the list of system variables in Appendix A, the two lists are not identical. Be sure you're referring to the proper list. 6 (C) Drawing Interchange and File Formats $ACADVER 1 the AutoCAD drawing database version number. $ANGBASE 50 Angle 0 direction. $ANGDIR 70 1=clockwise angles, 0=counterclockwise. $ATTDIA 70 Attribute entry dialogues, 1 = on, 0 = off $ATTMODE 70 Attribute visibility: 0=none, 1=normal, 2=all. $ATTREQ 70 Attribute prompting during INSERT, 1 = on, 0 = off $AUNITS 70 UNITS format for angles. $AUPREC 70 UNITS precision for angles. $AXISMODE 70 axis on if nonzero. $AXISUNIT 10,20 axis X and Y tick spacing. $BLIPMODE 70 blip mode on if nonzero. $CECOLOR 62 entity color number; 0 = BYBLOCK, 256 = BYLAYER. $CELTYPE 6 entity linetype name, or BYBLOCK or BYLAYER. $CHAMFERA 40 first chamfer distance. $CHAMFERB 40 second chamfer distance. $CLAYER 8 current layer name. $COORDS 70 0=static coordinate display, 1=continuous update, 2="d. The elapsed time variables ($TDINDWG and $TDUSRTIMER) have a similar format: . C.1.5.2 TABLES Section The TABLES section contains several tables, each of which in turn contains a variable number of table entries. The order of the tables may change, but the LTYPE table will always precede the LAYER table. Each table is intro- duced with a 0 group with the label "TABLE". This is followed by a 2 group identifying the particular table (VPORT, LTYPE, LAYER, STYLE, VIEW, UCS, or DWGMGR) and a 70 group that specifies the maximum number of table entries 9 AutoCAD Reference Manual that may follow. The tables in a drawing may contain deleted items, but these are not written to the DXF file. Thus, fewer table entries may follow the table header than are indicated by the 70 group, so don't use the count in the 70 group as an index to read in the table. It is provided so that your program to read DXF files can allocate an array in advance large enough to hold all the table entries that follow. Following this header for each table are the table entries. Each table item consists of a 0 group identifying the item type (same as table name, e.g., "LTYPE" or "LAYER"), a 2 group giving the name of the table entry, a 70 group specifying flags relevant to the table entry (defined for each table below), and additional groups that give the value of the table entry. The end of each table is indicated by a 0 group with the value "ENDTAB". If any table entry has bit value 64 set in its group 70 flags, the table entry was referenced by at least one entity in the drawing the last time the drawing editor was entered to edit this drawing. This "referenced" flag is for the benefit of the PURGE command; it can be ignored by most programs that read DXF files, and need not be set by programs that write DXF files. The following are the groups used for each type of table item. All groups are present for each table item. LTYPE 3 (descriptive text for linetype), 72 (alignment code), 73 (number of dash length items), 40 (total pattern length), 49 (dash length 1), 49 (dash length 2), . . . LAYER 62 (color number, negative if layer is off), 6 (linetype name). The 1 bit is set in the 70 group flags if the layer is frozen. STYLE 40 (fixed text height; 0 if not fixed), 41 (width factor), 50 (obliquing angle), 71 (text generation flags), 42 (last height used), 3 (primary font file name), 4 ("bigfont" file name; blank if none). If the third bit (4) is set in the 70 group flags, this is a vertically-oriented text style. A STYLE table item is used to record shape file LOAD requests also. In this case the first bit (1) is set in the 70 group flags and only the 3 group (shape file name) is meaningful (all the other groups are output, however). The "text generation flags" are a bit-coded field with the following bit meanings: Flag bit value Meaning 2 Text is backwards (mirrored in X) 4 Text is upside down (mirrored in Y) 10 (C) Drawing Interchange and File Formats VIEW 40 and 41 (view height and width), 10 and 20 (view center point), 11, 21, 31 (view direction from target, in WCS), 12, 22, 32 (target point, in WCS), 42 (lens length), 43 and 44 (front and back clipping planes-offsets from target point), 50 (twist angle), 71 view mode (see VIEWMODE system variable Appendix A). UCS 10, 20, 30 (origin), 11, 21, 31 (X axis direction), 12, 22, 32 (Y axis direction). All in World coordinates. VPORT 10 and 20 (lower left corner of viewport; 0.0 to 1.0), 11 and 21 (upper right corner), 12 and 22 (view center point), 13 and 23 (snap base point), 14 and 24 (snap spacing, X and Y), 15 and 25 (grid spacing, X and Y), 16, 26, 36 (view direction from target point), 17, 27, 37 (view target point), 40 (view height), 41 (viewport aspect ratio), 42 (lens length), 43 and 44 (front and back clipping planes; offsets from target point), 50 (snap rotation angle), 51 (view twist angle), 71 (view mode; see VIEWMODE system variable in Appendix A), 72 (circle zoom percent), 73 (fast zoom setting), 74 (UCSICON setting), 75 (snap on/off), 76 (grid on/off), 77 (snap style), 78 (snap isopair). The VPORT table is unique in that it may contain several entries with the same name (indicating a multiple-viewport configuration). The entries corresponding to the active view- port configuration all have the name "*ACTIVE". The first such entry describes the current viewport. DWGMGR For future use. Fields not yet defined. C.1.5.3 BLOCKS Section The BLOCKS section of the DXF file contains all the Block Definitions. This section contains the entities that make up the Blocks used in the drawing, including "anonymous" Blocks generated by the HATCH command and by associative dimensioning. The format of the entities in this section is identical to those in the ENTITIES section described below, so refer to that section for details. All entities in the BLOCKS section appear between BLOCK and ENDBLK entities. BLOCK and ENDBLK entities appear only in the BLOCKS section. Block definitions are never nested (that is, no BLOCK or ENDBLK entity ever appears within another BLOCK-ENDBLK pair). C.1.5.4 ENTITIES Section Entity items appear in both the BLOCK and ENTITIES sections of the DXF file. The appearance of entities in the two sections is identical, with the exception that entities in the BLOCK section never have handles. The following gives the format of each entity as it appears in the file. Some groups that define an entity always appear, and some are optional and appear only if they differ from their default values. In the following 11 AutoCAD Reference Manual discussion, groups that always occur are given by their group number and function, while optional groups are indicated by "-optional N" following the group description. "N" is the default value if the group is omitted. Programs that read DXF files should not assume that the groups describing an entity occur in the order given here. The end of the groups that make up an entity is indicated by the next 0 group, beginning the next entity or indicating the end of the section. Remember that a DXF file is a complete representation of the drawing data- base, and that as AutoCAD is further enhanced, new groups will be added to entities to accommodate additional features. Writing your DXF processing program in a table-driven way, making no assumptions about the order of groups in an entity, and ignoring any groups not presently defined, will make it much easier to accommodate DXF files from future releases of AutoCAD. Each entity begins with a 0 group identifying the entity type. The names used for the entities are given in the table that follows. Every entity contains an 8 group that gives the name of the layer on which the entity resides. Each entity may have elevation, thickness, linetype, or color information associated with it. If handles are enabled, every entity has a 5 group containing its handle (as a string representing a hexadecimal number). The following groups are included only if the entity has nonde- fault values for these properties. Group code Meaning 6 Linetype name (if not "BYLAYER"). The special name "BYBLOCK" indicates a floating linetype. 38 Elevation (if nonzero). Output only if system variable FLATLAND is 1. Otherwise, Z coordinates are supplied as 3x-groups as part of each of the entity's defining points. 39 Thickness (if nonzero). 62 Color number (if not "BYLAYER"). Zero indicates the "BYBLOCK" (floating) color. 210, These groups are included for each Line, Point, Circle, Shape, 220, Text, Arc, Trace, Solid, Block Reference, Polyline, Dimension, 230 Attribute, and Attribute Definition entity if its extrusion direction is not parallel to the World Z axis. The indicate the X, Y, and Z components of the entity's extrusion direction. The rest of the groups that make up an entity item are described below. Many of the entities include "flag" groups. These are integer codes (6x or 7x groups) that encode various pieces of information regarding the entity, and are specific to the particular entity type. In the following descrip- tions, the term "bit-coded" means that the flag contains various true/false values coded as the sum of the bit values given. Any bits not defined in the following section should be ignored in these fields and set to zero when constructing a DXF file. 12 (C) Drawing Interchange and File Formats LINE 10, 20, 30 (start point), 11, 21, 31 (end point). POINT 10, 20, 30 (point), 50 (angle of X axis for the UCS in effect when the Point was drawn -optional 0, for use when PDMODE is nonzero). CIRCLE 10, 20, 30 (center), 40 (radius). ARC 10, 20, 30 (center), 40 (radius), 50 (start angle), 51 (end angle). TRACE Four points defining the corners of the trace: (10, 20, 30), (11, 21, 31), (12, 22, 32), and (13, 23, 33). SOLID Four points defining the corners of the solid: (10, 20, 30), (11, 21, 31), (12, 22, 32), and (13, 23, 33). If only three points were entered (forming a triangular solid), the third and fourth points will be the same. TEXT 10, 20, 30 (insertion point), 40 (height), 1 (text value), 50 (rotation angle -optional 0), 41 (relative X scale factor -optional 1), 51 (obliquing angle -optional 0), 7 (text style name -optional "STANDARD"), 71 (text generation flags -optional 0), 72 (justification type -optional 0), 11, 21, 31 (alignment point -optional, appears only if 72 group is present and nonzero). The "text generation flags" are a bit-coded field with mean- ings as follows: Flag bit value Meaning 2 Text is backwards (mirrored in X) 4 Text is upside down (mirrored in Y) The "justification type" value (not bit-coded) indicates the text justification style used on this entity, as shown in the following table. Value Meaning 0 Text is left justified 1 Text is centered along its baseline 2 Text is right justified 3 Text is aligned between two points (height varies) 4 Text is "middle" (fully) centered 5 Text is fit between two points (width varies) If the justification is anything other than 0 (left justi- fied), 11, 21, and 31 groups will also appear in the entity to specify the alignment point of the text (center, right- most, or second alignment point). DXFOUT handles ASCII control characters in text strings by expanding the character into a "^" (caret) followed by the 13 AutoCAD Reference Manual appropriate letter. For example, an ASCII Control-G (BEL, decimal code 7) is output as "^G". If the text itself con- tains a caret character, it is expanded to "^ " (caret, space). DXFIN performs the complementary conversion. SHAPE 10, 20, 30 (insertion point), 40 (size), 2 (shape name), 50 (rotation angle -optional 0), 41 (relative X scale factor -optional 1), 51 (obliquing angle -optional 0). BLOCK 2 (Block name), 70 (Block type flags), 10, 20, 30 (Block base point). Appears only in BLOCKS section. The "Block type flags" are bit-coded, with the following bit meanings: Flag bit value Meaning 1 This is an "anonymous" Block generated by hatching, associative dimensioning, or other internal operations. 2 This Block has Attributes. ENDBLK No groups. Appears only in BLOCKS section. INSERT 66 ("Attributes follow" flag -optional 0), 2 (Block name), 10, 20, 30 (insertion point), 41 (X scale factor -optional 1), 42 (Y scale factor -optional 1), 43 (Z scale factor -optional 1), 50 (rotation angle -optional 0), 70 and 71 (column and row counts -optional 1), 44 and 45 (column and row spacing -optional 0). If the value of the "Attributes follow" flag is 1, a series of Attribute (ATTRIB) entities is expected to follow the INSERT, terminated by a sequence end (SEQEND) entity. ATTDEF 10, 20, 30 (text start), 40 (text height), 1 (default value, see TEXT above for handling of ASCII control characters), 3 (prompt string), 2 (tag string), 70 (Attribute flags), 73 (field length -optional 0), 50 (text rotation -optional 0), 41 (relative X scale factor -optional 1), 51 (obliquing angle -optional 0), 7 (text style name -optional "STANDARD"), 71 (text generation flags -optional 0, see TEXT above), 72 (text justification type -optional 0, see TEXT above)), 11, 21, 31 (alignment point -optional, appears only if 72 group is present and nonzero). The "Attribute flags" are a bit-coded field in which the bits have the following meanings: Flag bit value Meaning 1 Attribute is invisible (does not display) 2 This is a constant Attribute 4 Verification is required on input of this Attribute. 8 Attribute is preset (no prompt during insertion) 14 (C) Drawing Interchange and File Formats ATTRIB 10, 20, 30 (text start), 40 (text height), 1 (value, see TEXT above for handling of ASCII control characters), 2 (Attribute tag), 70 (Attribute flags; see ATTDEF above), 73 (field length -optional 0), 50 (text rotation -optional 0), 41 (rel- ative X scale factor -optional 1), 51 (obliquing angle -optional 0), 7 (text style name -optional "STANDARD"), 71 (text generation flags -optional 0, see TEXT above), 72 (text justification type -optional 0, see TEXT above), 11, 21, 31 (alignment point -optional, appears only if 72 group is present and nonzero). POLYLINE 66 ("vertices follow flag"), 70 (Polyline flags), 40 (default starting width), 41 (default ending width), 71 and 72 (poly- gon mesh M and N vertex counts -optional 0), 73 and 74 (smooth surface M and N densities -optional 0), 75 (smooth surface type -optional 0). The default widths apply to any vertex that doesn't supply widths (see below). The "vertices follow" flag is always 1, indicating that a series of VERTEX entities is expected to follow the POLYLINE, terminated by a sequence end (SEQEND) entity. The "polyline flags" group is a bit-coded field with bits defined as fol- lows: Flag bit value Meaning 1 This is a closed Polyline (or a polygon mesh closed in the M direction) 2 Curve-fit vertices have been added 4 Spline-fit vertices have been added 8 This is a 3D Polyline 16 This is a 3D polygon mesh. Group 75 indi- cates the smooth surface type, as follows: 0 = no smooth surface fitted 5 = quadratic B-spline surface 6 = cubic B-spline surface 8 = Bezier surface 32 The polygon mesh is closed in the N direc- tion VERTEX 10, 20, 30 (location), 40 (starting width -optional, see above), 41 (ending width -optional, see above), 42 (bulge), 70 (vertex flags), 50 (curve fit tangent direction -optional). The bulge is the tangent of 1/4 the included angle for an arc segment, made negative if the arc goes clockwise from the start point to the end point; a bulge of 0 indicates a straight segment, and a bulge of 1 is a semicir- cle. The meanings of the bit-coded "vertex flags" are shown in the following table. 15 AutoCAD Reference Manual Flag bit value Meaning 1 Extra vertex created by curve fitting 2 Curve fit tangent defined for this vertex. A curve fit tangent direction of 0 may be omitted from the DXF output, but is signif- icant if this bit is set. 4 Unused (never set in DXF files) 8 Spline vertex created by spline fitting 16 Spline frame control point 32 3D Polyline vertex 64 3D polygon mesh vertex SEQEND No fields. This entity marks the end of vertices (VERTEX type name) for a Polyline, or the end of Attribute entities (ATTRIB type name) for an INSERT entity that has Attributes (indicated by 66 group present and nonzero in INSERT entity). 3DLINE 10, 20, 30 (start point), 11, 21, 31 (end point). 3DFACE Four points defining the corners of the face: (10, 20, 30), (11, 21, 31), (12, 22, 32), and (13, 23, 33). 70 (invisible edge flags -optional 0). If only three points were entered (forming a triangular face), the third and fourth points will be the same. The meanings of the bit-coded "invisible edge flags" are shown in the following table. Flag bit value Meaning 1 First edge is invisible 2 Second edge is invisible 4 Third edge is invisible 8 Fourth edge is invisible DIMENSION 2 (name of pseudo-Block containing the current dimension pic- ture), 10, 20, 30 (definition point for all dimension types), 11, 21, 31 (middle point of dimension text), 12, 22, 32 (insertion point for clones of a dimension (for BASELINE and CONTINUE), 70 (Dimension type; 0=rotated, horizontal, or ver- tical; 1=aligned; 2=angular; 3=diameter; 4=radius - the value 128 is added to this field if the dimension text has been positioned at a user-defined location rather than at the default location), 1 (dimension text explicitly entered by the user. If null, the dimension measurement is drawn as the text. Otherwise, this text is drawn (but if it includes the sequence "<>", the dimension measurement is drawn in place of the "<>")), 13, 23, 33 (definition point for linear and angu- lar dimensions), 14, 24, 34 (definition point for linear and angular dimensions), 15, 25, 35 (definition point for diame- ter, radius, and angular dimensions), 16, 26, 36 (point defining dimension arc for angular dimensions), 40 (leader length for radius and diameter dimensions), 50 (angle of rotated, horizontal, or vertical linear dimensions). 16 (C) Drawing Interchange and File Formats In addition, all dimension types have an optional group (code 51) that indicates the "horizontal" direction for the Dimen- sion entity. This determines the orientation of dimension text and dimension lines for horizontal, vertical and rotated linear dimensions. The group value is the negative of the ECS angle of the UCS X axis in effect when the Dimension was drawn. In other words, the X axis of the UCS in effect when the Dimension was drawn is always parallel to the XY plane for the Dimension's ECS, and the angle between the UCS X axis and the ECS X axis is a single 2D angle. The value in group 51 is the angle from "horizontal" (the effective X axis) to the ECS X axis. Entity Coordinate Systems (ECS) are described later in this section. For all dimension types, the following groups represent 3D WCS points, regardless of the FLATLAND setting. 10, 20, 30 13, 23, 33 14, 24, 34 15, 25, 35 For all dimension types, the following groups represent ECS points, and are 2D or 3D depending on the FLATLAND setting. 11, 21(, 31) 12, 22(, 32) 16, 26(, 36) Linear (13,23,33) The point used to specify the first extension line. (14,24,34) The point used to specify the second extension line. (10,20,30) The point used to specify the dimension line. Angular (13,23,33) and (14,24,34) The endpoints of the first line (10,20,30) and (15,25,35) The endpoints of the second line (16,26,36) The point used to specify the dimen- sion line arc Diameter (15,25,35) The point used to pick the circle/arc to dimension (10,20,30) The point on that circle directly across from the pick point. Radius (15,25,35) The point used to pick the circle/arc to dimension (10,20,30) The center of that circle. 17 AutoCAD Reference Manual Entity Coordinate Systems (ECS) To save space in the drawing database (and in the DXF file), the points associated with each entity are expressed in terms of its own Entity Coor- dinate System (ECS). The Entity Coordinate System allows AutoCAD to use a much more compact means of representation for entities. With ECS, the only additional information needed to describe its position in 3D space is the 3D vector describing the Z axis of the ECS, and the elevation value. For a given Z axis (or extrusion) direction, there is an infinite number of coordinate systems, defined by translating the origin in 3D space and by rotating the X and Y axes around the Z axis. However, for the same Z axis direction, there is only one Entity Coordinate System. It has the follow- ing properties: o Its origin coincides with the WCS origin. o The orientation of the X and Y axes within the XY plane are calcu- lated in an arbitrary, but consistent manner. AutoCAD performs this calculation using the "arbitrary axis" algorithm described below. For some entities, the ECS is equivalent to the World Coordinate System and all points (DXF groups 10-37) are expressed in World coordinates. See the following table. Entities Notes LINE, POINT, 3DFACE, 3D These entities do not lie in Polyline, 3D Vertex, 3D a particular plane. All Mesh, 3D Mesh vertex points are expressed in World coordinates. Of these entities, only Lines and Points can be extruded; their extrusion direction can differ from the World Z axis. CIRCLE, ARC, SOLID, TRACE, These entities are planar in TEXT, ATTRIB, ATTDEF, SHAPE, nature. All points are INSERT, 2D Polyline, 2D expressed in Entity coordi- Vertex nates. All these entities can be extruded; their extrusion direction can differ from the World Z axis. DIMENSION Some of a Dimension's points are expressed in WCS, and some in ECS. Others The remaining entities have no point data and their coordinate systems are therefore irrelevant. Once AutoCAD has established the ECS for a given entity, here's how it works: 18 (C) Drawing Interchange and File Formats o The elevation value stored with an entity indicates how far along the Z axis to shift the XY plane from the WCS origin to make it coincide with the plane that the entity is in. How much of this is the user-defined elevation is unimportant. o Any 2D points describing the entity that were entered through the UCS are transformed into the corresponding 2D points in the ECS, which (more often than not) is shifted and rotated with respect to the UCS. A few ramifications of this process are: o You can not reliably find out what UCS was in effect when an entity was acquired. You can only find out where the entity is in the current UCS if the current UCS has the same Z axis direction as the original UCS (i.e., they both reduce to the same ECS). o When you enter the XY coordinates of an entity in a given UCS and then do a DXFOUT, you probably won't recognize those XY coordi- nates in the DXF file. You'll have to know the method by which AutoCAD calculates the X and Y axes in order to work with these values. o The elevation value stored with an entity and output in DXF files will be a sum of the Z coordinate difference between the UCS XY plane and the ECS XY plane, and the elevation value that the user specified at the time the entity was drawn. Arbitrary Axis Algorithm The arbitrary axis algorithm is used by AutoCAD internally to implement the "arbitrary but consistent" generation of Entity Coordinate Systems for all entities except Lines, Points, 3D Faces, and 3D Polylines, which contain points in World coordinates. Given a unit-length vector to be used as the Z axis of a coordinate system, the arbitrary axis algorithm generates a corresponding X axis for the coor- dinate system. The Y axis follows by application of the right hand rule. The method is to examine the given Z axis (also called the normal vector) and see if it is close to the positive or negative World Z axis. If it is, cross the World Y axis with the given Z axis to arrive at the arbitrary X axis. If not, cross the World Z axis with the given Z axis to arrive at the arbitrary X axis. The boundary at which the decision is made was chosen to be both inexpensive to calculate and completely portable across machines. This is achieved by having a sort of "square" polar cap, the bounds of which is 1/64, which is precisely specifiable in 6 decimal frac- tion digits and in 6 binary fraction bits. In mathematical terms, the algorithm does the following (all "vectors" are assumed to be in 3D space, specified in the World Coordinate System). Let the given normal vector be called N. Let the World Y axis be called Wy, which is always (0,1,0). Let the World Z axis be called Wz, which is always (0,0,1). 19 AutoCAD Reference Manual We are looking for the arbitrary X and Y axes to go with the normal N. They'll be called Ax and Ay. N could also be called Az (the arbitrary Z axis). If (Nx < 1/64) and (Ny < 1/64) then Ax = Wy * N (where "*" is the cross-product operator). Otherwise, Ax = Wz * N. Scale Ax to unit length. The method of getting the Ay vector would be: Ay = N * Ax. Scale Ay to unit length. C.1.6 Writing DXF Interface Programs Writing a program that communicates with AutoCAD via the DXF mechanism often appears far more difficult than it really is. The DXF file contains a seemingly overwhelming amount of information, and examining a DXF file manually may lead to the conclusion that the task is hopeless. However, the DXF file has been designed to be easy to process by program, not manually. The format was constructed with the deliberate intention of making it easy to ignore information you don't care about while easily reading the information you need. Just remember to handle the groups in any order and ignore any group you don't care about, and you'll be home free. As an example, the following is a Microsoft BASIC program that reads a DXF file and extracts all the LINE entities from the drawing (ignoring lines that appear inside Blocks). It prints the endpoints of these lines on the screen. As an exercise you might try entering this program into your com- puter, running it on a DXF file from one of your drawings, then enhancing it to print the center point and radius of any circles it encounters. This program is not put forward as an example of clean programming technique nor the way a general DXF processor should be written; it is presented as an example of just how simple a DXF-reading program can be. 1000 REM 1010 REM Extract lines from DXF file 1020 REM 1030 G1% = 0 1040 LINE INPUT "DXF file name: "; A$ 1050 OPEN "i", 1, A$ + ".dxf" 1060 REM 1070 REM Ignore until section start encountered 1080 REM 1090 GOSUB 2000 1100 IF G% <> 0 THEN 1090 1110 IF S$ <> "SECTION" THEN 1090 20 (C) Drawing Interchange and File Formats 1120 GOSUB 2000 1130 REM 1140 REM Skip unless ENTITIES section 1150 REM 1160 IF S$ <> "ENTITIES" THEN 1090 1170 REM 1180 REM Scan until end of section, processing LINEs 1190 REM 1200 GOSUB 2000 1210 IF G% = 0 AND S$ = "ENDSEC" THEN 2200 1220 IF G% = 0 AND S$ = "LINE" THEN GOSUB 1400 : GOTO 1210 1230 GOTO 1200 1400 REM 1410 REM Accumulate LINE entity groups 1420 REM 1430 GOSUB 2000 1440 IF G% = 10 THEN X1 = X : Y1 = Y : Z1 = Z 1450 IF G% = 11 THEN X2 = X : Y2 = Y : Z2 = Z 1460 IF G% = 0 THEN PRINT "Line from (";X1;",";Y1;",";Z1;") to (";X2; ",";Y2;",";Z2;") 1470 GOTO 1430 2000 REM 2010 REM Read group code and following value 2020 REM For X coordinates, read Y and possibly Z also 2030 REM 2040 IF G1% < 0 THEN G% = -G1% : G1% = 0 ELSE INPUT #1, G% 2050 IF G% < 10 OR G% = 999 THEN LINE INPUT #1, S$ : RETURN 2060 IF G% >= 38 AND G% <= 49 THEN INPUT #1, V : RETURN 2080 IF G% >= 50 AND G% <= 59 THEN INPUT #1, A : RETURN 2090 IF G% >= 60 AND G% <= 69 THEN INPUT #1, P% : RETURN 2100 IF G% >= 70 AND G% <= 79 THEN INPUT #1, F% : RETURN 2110 IF G% >= 210 AND G% <= 219 THEN 2130 2120 IF G% >= 20 THEN PRINT "Invalid group code";G% : STOP 2130 INPUT #1, X 2140 INPUT #1, G1% 2150 IF G1% <> (G%+10) THEN PRINT "Invalid Y coord code";G1% : STOP 2160 INPUT #1, Y 2170 INPUT #1, G1% 2180 IF G1% <> (G%+20) THEN G1% = -G1% ELSE INPUT #1, Z 2190 RETURN 2200 CLOSE 1 Writing a program that constructs a DXF file is more difficult, because you must maintain consistency within the drawing in order for AutoCAD to find it acceptable. AutoCAD allows you to omit many items in a DXF file and still obtain a usable drawing. The entire HEADER section can be omitted if you don't need to set any header variables. Any of the tables in the TABLES section can be omitted if you don't need to make any entries, and in fact the entire TABLES section can be dropped if nothing in it is required. If you define any linetypes in the LTYPE table, this table must appear before the LAYER table. If no Block Definitions are used in the drawing, the BLOCKS section can be omitted. If present, however, it must appear before the ENTITIES section. Within the ENTITIES section, you can refer- ence layer names even though you haven't defined them in the LAYER table. 21 AutoCAD Reference Manual Such layers will be automatically created with color 7 and the CONTINUOUS linetype. The EOF item must be present at the end of file. The following Microsoft BASIC program constructs a DXF file representing a polygon with a specified number of sides, leftmost origin point, and side length. This program supplies only the ENTITIES section of the DXF file, and places all entities generated on the default layer "0". This may be taken as an example of a minimum DXF generation program. Since this pro- gram doesn't create the drawing header, the drawing limits, extents, and current view will be invalid after performing a DXFIN on the drawing gener- ated by this program. You can do a "ZOOM E" to fill the screen with the drawing generated. Then adjust the limits manually. 1000 REM 1010 REM Polygon generator 1020 REM 1030 LINE INPUT "Drawing (DXF) file name: "; A$ 1040 OPEN "o", 1, A$ + ".dxf" 1050 PRINT #1, 0 1060 PRINT #1, "SECTION" 1070 PRINT #1, 2 1080 PRINT #1, "ENTITIES" 1090 PI = ATN(1) * 4 1100 INPUT "Number of sides for polygon: "; S% 1110 INPUT "Starting point (X,Y): "; X, Y 1120 INPUT "Polygon side: "; D 1130 A1 = (2 * PI) / S% 1140 A = PI / 2 1150 FOR I% = 1 TO S% 1160 PRINT #1, 0 1170 PRINT #1, "LINE" 1180 PRINT #1, 8 1190 PRINT #1, "0" 1200 PRINT #1, 10 1210 PRINT #1, X 1220 PRINT #1, 20 1230 PRINT #1, Y 1240 PRINT #1, 30 1250 PRINT #1, 0.0 1260 NX = D * COS(A) + X 1270 NY = D * SIN(A) + Y 1280 PRINT #1, 11 1290 PRINT #1, NX 1300 PRINT #1, 21 1310 PRINT #1, NY 1320 PRINT #1, 31 1330 PRINT #1, 0.0 1340 X = NX 1350 Y = NY 1360 A = A + A1 1370 NEXT I% 1380 PRINT #1, 0 1390 PRINT #1, "ENDSEC" 22 (C) Drawing Interchange and File Formats 1400 PRINT #1, 0 1410 PRINT #1, "EOF" 1420 CLOSE 1 The DXFIN command is relatively forgiving with respect to the format of data items. As long as a properly formatted item appears on the line on which the data is expected, DXFIN will accept it (of course, string items should not have leading spaces unless these are intended to be part of the string). The above program takes advantage of this flexibility in input format, and does not go to great effort to generate a file appearing exactly like one generated by AutoCAD. In the case of error loading a DXF file using DXFIN, AutoCAD reports the error with a message indicating the nature of the error detected and the last line processed in the DXF file before the error was detected. This may not be the line on which the error occurred, especially in the case of such errors as omission of required groups. C.2 Binary Drawing Interchange Files The ASCII DXF file format described in the preceding sections of this appendix is a complete representation of an AutoCAD drawing in an ASCII text form easily processed by other programs. In addition, AutoCAD can produce or read a binary form of the full DXF file, and accepts limited input in another binary file format. These binary files are described in the following sections. C.2.1 Binary DXF Files The DXFOUT command provides a "Binary" option that writes binary DXF files. Such a file contains all of the information present in an ASCII DXF file, but in a much more compact form that takes, typically, 25% less file space and can be read and written more quickly (typically 5 times faster) by AutoCAD. Unlike ASCII DXF files, which entail a trade-off between size and floating-point accuracy, binary DXF files preserve all of the accuracy in the drawing database. AutoCAD Release 10 is the first version to support this form of DXF file; it cannot be read by older versions. A binary DXF file begins with a 22-byte sentinel consisting of: "AutoCAD Binary DXF" Following the sentinel are (group,value) pairs as in an ASCII DXF file, but represented in binary form. The group code is a single-byte binary value, and the value that follows is one of the following: o a two-byte integer with the least significant byte first and the most significant byte last, 23 AutoCAD Reference Manual o an eight-byte IEEE double precision floating-point number stored with the least significant byte first and the most significant byte last, or o an ASCII string terminated by a zero (NUL) byte. The type of the datum following a group is determined from the group code according to the same rules used in decoding ASCII DXF files. Translation of angles to degrees, and dates to fractional Julian date representation, is performed for binary files as well as for ASCII DXF files. The comment group, 999, is not used in binary DXF files. DXFOUT writes binary DXF files with the same file type (".dxf") as for ASCII DXF files. The DXFIN command automatically recognizes a binary file (by means of its sentinel string) and loads it. There is no need for you to identify it as a binary file. If DXFIN encounters an error in a binary DXF file, it reports the byte address within the file where the error was detected. C.3 Binary Drawing Interchange (DXB) Files The DXF file formats described earlier in this appendix are complete repre- sentations of an AutoCAD drawing that can be written and read by AutoCAD and other programs. However, AutoShade(tm) and programs executed via the "external commands" facility (Appendix B) often have a need to supply simple geometric input to AutoCAD. For these purposes, another file format even more compact than the binary DXF format is supported. This format, called DXB (for "drawing interchange binary") is limited in the entities it can represent. Furthermore, AutoCAD has a command to read such files, but no direct method of writing them. (The ADI plotter driver can plot to a file in DXB format.) C.3.1 DXBIN Command To load a DXB file produced by a program such as AutoShade, enter the DXBIN command: Command: DXBIN DXB file: enter the name of the file you wish to load. Don't include a file type; ".dxb" is assumed. 24 (C) Drawing Interchange and File Formats C.3.2 DXB File Format This information is for experienced programmers, and is subject to change without notice. The format of a DXB file is as follows: Header: "AutoCAD DXB 1.0" CR LF ^Z NUL (19 bytes) Data: . . . Zero or more data records . . . Terminator: NUL (1 byte) Each data record begins with a single byte giving its type, followed by data items. The data items have various forms of representation and encod- ing. In the descriptions below, each data item is prefixed with a letter and a hyphen. The meaning of the letter codes is as follows: w- 16-bit integer, byte reversed in the standard 8086 style (least significant byte first, most significant byte second). f- IEEE 64-bit floating-point value stored with lsb first, msb last (as stored by an 8087). l- 32-bit integer with the bytes reversed 8086-style. n- Number which may be either a 16-bit integer or a floating-point number depending on the most recent setting of the "number mode" data item. The number mode defaults to 0, signifying integers. If set to 1, all n- items will be read as floating-point. u- Item which is either a 32-bit integer or a floating-point number depending on the most recent number mode setting. If a 32-bit integer, the value is scaled by multiplying it by 65536 (2^16). If a floating-point value, no scaling is applied. a- Item representing an angle. If number mode is integer, this is a 32-bit integer representing an angle in units of millionths of a degree (range 0 to 360,000,000). If a floating-point number, rep- resents degrees. In the following table, the lengths include the item-type byte and assume the number mode is set to zero (integer mode). If number mode is floating- point, add 6 bytes to the length for each n- item present and 4 bytes for each a-, or u- item present. Item type Code Data items Length (decimal) (bytes) LINE 1 n-fromx n-fromy 9 n-tox n-toy POINT 2 n-x n-y 5 CIRCLE 3 n-ctrx n-ctry n-rad 7 ARC 8 n-ctrx n-ctry n-rad 19 a-starta a-enda continued ... 25 AutoCAD Reference Manual Item type Code Data items Length (decimal) (bytes) TRACE 9 n-x1 n-y1 n-x2 n-y2 17 n-x3 n-y3 n-x4 n-y4 SOLID 11 n-x1 n-y1 n-x2 n-y2 17 n-x3 n-y3 n-x4 n-y4 SEQEND 17 (none) 1 POLYLINE 19 w-closureflag 3 VERTEX 20 n-x n-y 5 3DLINE 21 n-fromx n-fromy n-fromz 13 n-tox n-toy n-toz 3DFACE 22 n-x1 n-y1 n-z1 25 n-x2 n-y2 n-x2 n-x3 n-y3 n-z3 n-x4 n-y4 n-z4 SCALE FACTOR 128 f-scalefac 9 NEW LAYER 129 "layername" NUL "layername" length + 2 LINE EXTENSION 130 n-tox n-toy 5 TRACE EXTENSION 131 n-x3 n-y3 n-x4 n-y4 9 BLOCK BASE 132 n-bx n-by 5 BULGE 133 u-2h/d 5 WIDTH 134 n-startw n-endw 5 NUMBER MODE 135 w-mode 3 NEW COLOR 136 w-colornum 3 3DLINE EXTENSION 137 n-tox n-toy n-toz 7 The LINE EXTENSION item extends the last line or line extension from its "to" point to a new "to point". The trace extension item similarly extends the last trace solid, or trace extension from its x3,y3-x4,y4 ending line to a new x3,y3-x4,y4 line. The SCALE FACTOR is a floating-point value by which all integer coordinates are multiplied to obtain the floating-point coordinates used by the actual entities. The initial scale factor when a file is read is 1.0. The NEW LAYER item will create a layer if none exists, giving it the same defaults as the "LAYER NEW" command, and will set that layer as the current layer for subsequent entities. At the end of the DXB file load, the layer in effect before the command will be restored. The BLOCK BASE item specifies the base (origin) point of a Block being cre- ated. The Block base must be defined before the first entity record is encountered. If DXB is not defining a Block, this specification will be ignored. A Polyline consists of straight segments of fixed width connecting the ver- tices, except as overridden by the BULGE and WIDTH items described below. The closure flag should be 0 or 1; if it is 1, then there is an implicit segment from the last vertex (immediately before the SEQEND) to the first vertex. A BULGE item, encountered between two VERTEX items (or after the last VERTEX of a closed Polyline), indicates that the two vertices are connected 26 (C) Drawing Interchange and File Formats by an arc rather than a straight segment. If the line segment connecting the vertices would have length d, and the perpendicular distance from the midpoint of that segment to the arc is h, then the magnitude of the BULGE is (2 * h / d). The sign is negative if the arc from the first vertex to the second is clockwise. A semicircle thus has a bulge of 1 (or -1). If the number mode is 0 (integer), BULGE items are scaled by 216. If the number mode has been set to floating-point, then the floating-point value supplied is just 2*h/d (not scaled). The WIDTH item indicates the starting and ending widths of the segment (straight or curved) connecting two vertices. This width stays in effect until the next width item or the SEQEND. If there is a WIDTH item between the POLYLINE item and the first VERTEX, it is stored as a default width for the Polyline; this will save considerable database space if the Polyline has several segments of this width. The NUMBER MODE item controls the mode of items with types given in the table above as n-, a-, or u-. If the value supplied is zero, these values will be integers, otherwise floating-point. The storage and implicit scal- ing conventions for these values in both modes are described above. LINEs and 3DLINEs share the same cells to remember the last to-point, so you shouldn't mix extension groups for the two entities without an initial group before the extension. There is no "extension" group for 3DFACEs, as there's no obvious edge to extend from. The "NEW COLOR" group specifies the color for subsequent entities in the DXB file. The "w-colornum" word argument is in the range from 0 to 256. 0 means color by block, 1-255 are the standard AutoCAD colors, and 256 means color by layer. A color outside the range from 0 to 256 sets the color back to the current entity color (you can do this deliberately, and it can be quite handy). The initial entity color of material added by DXBIN is the current entity color. All points specified in the DXB file are interpreted in terms of the cur- rent UCS at the time the DXBIN command is executed. C.3.3 Writing DXB Files There is no direct AutoCAD command to write a DXB file, but the special "ADI" plotter driver can write such a file. If you want to create a DXB file from an AutoCAD drawing, configure the "ADI" plotter and select its DXB file output option. 27 AutoCAD Reference Manual C.4 Initial Graphics Exchange Standard (IGES) Files Using the commands described in this section, you can instruct AutoCAD to read and write IGES format interchange files. NOTE: The format of IGES files and the mapping performed to translate between AutoCAD drawing information and IGES are described in the separate AutoCAD / IGES Interface Specifications document (one of the items supplied when you return your AutoCAD license registration card). C.4.1 IGESOUT Command You can generate an Initial Graphics Exchange Standard (IGES) interchange file from an existing AutoCAD drawing by means of the Drawing Editor's IGESOUT command. The command format is: Command: IGESOUT File name: (name or RETURN) The default name for the output file is the same as that of the current drawing, but with a file type of ".igs". If you specify an explicit file name without including a file type, ".igs" is assumed. If a file with the same name already exists, it is deleted. C.4.2 IGESIN Command An IGES interchange file can be converted into an AutoCAD drawing by means of the IGESIN command. First enter the Drawing Editor using the "Create new drawing" task from the Main Menu. Then issue the IGESIN command. Command: IGESIN File name: (name) Enter the name of the IGES file to be loaded. If a serious error is encountered, the input process is halted and an error message is displayed reporting where the error was found. The partial drawing is not discarded. 28 (C) Drawing Interchange and File Formats C.5 Slide File Format AutoCAD slide files are screen images written by the MSLIDE command and read by the VSLIDE command. This section describes the format of slide files, for the benefit of developers who wish to incorporate support for AutoCAD slides into their programs. This information is for experienced programmers, and is subject to change without notice. The general format of a slide file is: 1. Header (31 bytes) 2. One or more data records (variable length) All coordinates and sizes written to the slide file reflect the graphics area of the display device from which the slide was created, with point (0,0) located at the lower left corner of the graphics area. For AutoCAD Release 9 and later, the slide file header consists of the following fields: Field Bytes Description Id string 17 "AutoCAD Slide" CR LF ^Z NUL Type indicator 1 Currently set to 86 (decimal). Level indicator 1 Currently set to 2. High X dot 2 Width of the graphics area - 1, in pixels. High Y dot 2 Height of the graphics area - 1, in pixels. Aspect ratio 4 Aspect ratio (horizontal size / vertical size in inches) of the graphics area, scaled by 10,000,000. This value is always written with the least significant byte first. Hardware fill 2 Either 0 or 2 (value is unimportant). Test number 2 A number (1234 hex) used to determine whether all 2-byte values in this slide file were writ- ten with the high byte first (as by Intel 8086-family CPUs) or the low byte first (as by Motorola 68000-family CPUs). Data records follow the header. Each data record begins with a 2-byte field whose high-order byte is the record type. The remainder of the record may be composed of 1-byte or 2-byte fields, as described in the fol- lowing table. To determine whether the 2-byte fields are written with the high byte first or the low byte first, examine the Test number field of the header, described above. 29 AutoCAD Reference Manual Record Length Meaning Description type (hex) (bytes) 00 - 7F 8 Vector The from-X coordinate for an ordinary vector. From-Y, to-X, and to-Y follow in that order, as 2-byte values. The from point is saved as the last point. 80 - FA - Undefined Reserved for future use. FB 5 Offset vector The low-order byte and the fol- lowing three bytes specify the endpoints (from-X, from-Y, to-X, to-Y) of a vector, in terms of offsets (-128 to +127) from the saved last point. The adjusted from point is saved as the last point for use by subsequent vec- tors. FC 2 End of file The low-order byte is 00. FD 6 Solid fill The low-order byte is always zero. The following two 2-byte values specify the X and Y coor- dinates of one vertex of a poly- gon to be solid-filled. 3 to 10 such records occur in sequence. A Solid fill record with a nega- tive Y coordinate indicates the start or end of such a flood sequence. In the start record, the X coordinate indicates the number of vertex records to follow. FE 3 Common This is a vector starting at the endpoint last point. The low-order byte vector and the following byte specify to-X and to-Y in terms of offsets (-128 to +127) from the saved last point. The adjusted to point is saved as the last point for use by subsequent vectors. FF 2 New color Subsequent vectors are to be drawn using the color number indicated by the low-order byte. If a slide contains any vectors at all, a New color record will be the first data record. The order of the vectors in a slide, and the order of the endpoints of those vectors, may vary. For example, the following is an annotated hex dump of a simple slide file created on an IBM PC/AT with an IBM Enhanced Graphics Adapter. The slide consists of a white diagonal line from the lower left corner to the upper right corner of the graphics area, a green vertical line near the lower left corner, and a small red rectangle at the lower left corner. 30 (C) Drawing Interchange and File Formats 41 75 74 6F 43 41 Id string ("AutoCAD Slide" CR LF ^Z NUL) 44 20 53 6C 69 64 65 0D 0A 1A 00 56 Type indicator (86) 02 Level indicator (2) 3C 02 High X dot (572) 24 01 High Y dot (292) 0B 80 DF 00 Aspect ratio (14,647,307 / 10,000,000 = 1.46) 02 00 Hardware fill (2) 34 12 Test number (1234 hex) 07 FF New color (7 = white) 3C 02 24 01 00 00 00 00 Vector from 572,292 to 0,0. 572,292 becomes "last" point 03 FF New color (3 = green) 0F 00 32 00 0F 00 13 00 Vector from 15,50 to 15,19. 15,50 becomes "last" point 01 FF New color (1 = red) 12 FB E7 12 CE Offset vector from 15+18,50-25 (33,25) to 15+18,50-50 (33,0). 33,25 becomes "last" point DF FE 00 Common-endpoint vector from 33,25 to 33-33,25+0 (0,25). 0,25 becomes "last" point 00 FE E7 Common-endpoint vector from (0,25) to 0+0,25-25 (0,0). 0,0 becomes "last" point 21 FE 00 Common-endpoint vector from (0,0) to 0+33,0+0 (33,0). 33,0 becomes "last" point 00 FC End of file Old Slide Header The slide format described above is that produced by AutoCAD Release 9 and later, and is portable among all computers running AutoCAD Release 9 or later. Previous versions of AutoCAD (as well as AutoShade 1.0 and AutoSketch 1.02) produce slides with a somewhat different header, as shown below. Field Bytes Description Id string 17 "AutoCAD Slide" CR LF ^Z NUL Type indicator 1 86 (decimal). Level indicator 1 1 (old format). High X dot 2 Width of the graphics area - 1, in pixels. High Y dot 2 Height of the graphics area - 1, in pixels. Aspect ratio 8 Aspect ratio (horizontal size / vertical size in inches) of the graphics area, written as a floating-point number. Hardware fill 2 Either 0 or 2 (value is unimportant). Filler byte 1 Unused Note that the old-format header does not contain a Test number field. The floating-point aspect ratio value and all two-byte integers are written in the native format of the CPU used to create the file (for 8086-family CPUs, IEEE double-precision and low byte first). Old-format slide files are not portable across machine types, but they can be read by any version of AutoCAD running on the same CPU type as the CPU with which the slide was created. 31 AutoCAD Reference Manual C.6 Slide Library File Format This section describes the format of AutoCAD slide libraries (Release 9 and later), for the benefit of developers who wish to incorporate support for slide libraries into their programs. This information is for experienced programmers, and is subject to change without notice. The general format of a slide library is: 1. Header (32 bytes) "AutoCAD Slide Library 1.0" CR LF ^Z NUL NUL NUL NUL 2. One or more slide directory entries (36 bytes each) 3. One or more slides (variable length) Slide directory entries have the following format: 1. Slide name (NUL terminated) (32 bytes) 2. Address of slide within library file (4 bytes) The slide address is always written with the low byte first. Each slide to which the directory points is a complete slide file as described in the previous section. The end of the slide directory is signified by an entry with a null slide name (first byte is NUL). A slide library may contain a mixture of old-format and new-format slides. 32 . 0f:.. "fRESOURCETXT bf; GEM OBJECTS AND RESOURCES ========================= By: Mrten Lindstrm This text just about sums up what I presently know about the subject and comes from many sources, perhaps most important of which are articles in the 'ST Magazin' - a German ST magazine now unfortunately dead. For the info about colour icons I admit to have drawn exclusively on the Atari Compendium. (I haven't got a TOS version capable of dealing with colour icons so with them I lack experiences of my own.) Any further info - or corrections - most welcome! ABBREVIATIONS: In the descriptions below I have used the following abbreviations: N = Nybble = half a byte (i.e. 4 bits or 1 hexadecimal digit) B = Byte W = Word = 2 bytes L = Longword = 4 bytes POINTERS: All pointers and address offsets in an RSC file are, on disk, relative to file start. Before use in memory, longword pointers have to be relocated, i.e. the file start address added to each pointer. This is done automati- cally by RSRC_LOAD. If you don't use RSRC_LOAD, but include an RSC file directly in your program code during assembly/compilation, you will have to make sure that the relocation is done by other means. One L pointer can be found at offset 12 in each object structure, except in those of the simple box types (20, 25 and 27). In a resource with images or formatted text objects, further pointers can be found in other structures (TEDINFO, BITBLK, ICONBLK). See more below. Note that WORD offsets are UNSIGNED, which means that they can be up to 64K (and also, in assembler terms, that a single Motorola instruction isn't enough to add them to the RSC base address; instead you have to load them in a cleared data register and add this - as a long - to the base address). MEASURES: Coordinates and dimensions are, in an RSC file on disk, measured in CHARACTERS, but note that the higher ordered byte is reserved for extra pixels; i.e. a height of $301 is to be read as 1 full character plus 3 extra pixels. (The exception is measures within the BITBLK and ICONBLK secondary structures for images and icons - see below.) When in use in memory, however, all measures are in PIXELS. So in ST colour resolutions a height of $301 will have been converted into 11 (8+3) while in ST high rez the same disk based height will have become 19 (16+3). You normally don't have to worry too much about how this conversion is performed since the AES will do it for you, either during RSC file loading with RSRC_LOAD, or you can call the function RSRC_OBFIX for each object. But it still is worth knowing. COLOURS: Any colour definition, in objects or secondary structures, is always contained in ONE NYBBLE. This gives a range of 16 possible colours: 0=White, 1=Black, 2=Red, 3=Green, 4=Blue, 5=Cyan, 6=Yellow, 7=Magenta 8=Light, 9=Dark, 10=Light, 11=Light, 12=Light, 13=Light, 14=Light, 15=Light gray gray red green blue cyan yellow magenta OBJECTS AND TREES: A GEM object is, on the screen, a box. The box can be invisible, can contain text, fill patterns etc. and can in some cases be assigned the function as a button to be clicked on. A GEM object tree is, on the screen, a collection of boxes within each other, together forming a dialogue box or a menu. Each of the inner boxes is termed 'CHILD' of the next outer box - the 'PARENT' - containing it. The outermost box is called the 'ROOT' of the tree. In program memory, or in a file, an object is a 12-word structure (24 bytes) which may contain a pointer to either a text string or to further structures. A tree, in program memory, is simply a list of object structures, the first of which is the root object, provided that the relational data for all objects are sensible. Each object is assigned a number according to its place in the tree object list (root=0), and these numbers are used in the relational data. The first word of each object specifies the next sibling or, for the last sibling, parent (or -1 for the root which has neither siblings nor parent). And word 2 and 3 give the first and last child of the object (-1 for childless objects). NOTE: Some experiments that I have made seem to indicate that not only do the objects of a tree need to be listed in one uninterrupted list of object specifications (the first of which must be the 'progenitor'), but the same applies for all 'sub-trees' within the tree. I.e. all children, 'grand- children' etc. of an object need to be listed immediately after itself and before a sibling or any of its offspring is listed. RESOURCE FILES: Anyone can create object trees directly in the source text of a program, in which case the one and only requirement on the order of the structures is the one just mentioned (for objects belonging to the same tree). As long as all pointers are right, different trees can be placed wherever desired, and so can all the other, secondary, structures. However, as a convenience, a set of AES routines exists to deal with a collection of many object trees with associated structures, strings and images placed together in an RSC FILE. The RSC file in addition has room for so called 'free' strings and images - i.e. not associated with the object-related structures; this makes it possible to include for instance alertbox definitions as well in an RSC file - as free strings. When an RSC file is loaded with RSRC_LOAD two modifications to the file data are, as mentioned, made automatically: 1) All measures are converted from CHARACTERS into PIXELS. 2) All Long pointers are relocated. Not using RSC_LOAD means these modifications must be made some other way. What is NOT made automatically is conversion of IMAGES/ICONS (apparently with the of exception colour icons in TOS 4) from the VDI standard format, used in the RSC file, into device specific format. This isn't a big problem since the images are 'monochrome' (1 plane images), which means that errors could occur only with certain graphic cards. Still, a perfectionist should let the VDI function VR_TRNFM have a go with each mono image in the RSC file. FORMAT OF RSC FILES ------------------- (All offsets are relative to file start, and UNSIGNED) HEADER: (18 words) W: RCS version number: bit 2 set => TOS 4+ (i.e. 0 or 1 = old, 4 = new) W: Offset to first object W: Offset to first TEDINFO (secondary structure for formatted text string) W: Offset to first ICONBLK (secondary structure for icon) W: Offset to first BITBLK (secondary structure for simple image) W: Offset to pointer table (L) - free strings (typically alertbox strings) W: Offset to first string - free or not W: Offset to first image data (or mask) - free or not W: Offset to pointer table (L) - free images W: Offset to pointer table (L) - object trees (i.e. root objects) W: Number of objects W: Number of trees W: Number of TEDINFOs W: Number of ICONBLKs W: Number of BITBLKs W: Number of free strings W: Number of free images W: File length in bytes (or offset to TOS 4+ extension see v number above) After this the structures come, apparently always in the following order (this order shouldn't be a requirement, but you never know): Strings (null-ended) Images BITBLKs (14 bytes each) ICONBLKs (34 bytes each) TEDINFOs (28 bytes each) Objects (trees) (24 bytes/object) Table of pointers (L) to object trees (i.e. to root objects) Table of pointers (L) to free strings Table of pointers (L) to free images The whole pre-TOS4 RSC file must always be less than 64K in size (since the file length is given in just a WORD in the header). --------------- EXTENSION (TOS 4+) may follow after above structures (see v number above): L: REAL length of the RSC file (no longer any 64K limit) L: Pointer to 'pointer table' to CICONBLKs (for colour icons) ?L: Possible further pointers which however not yet are defined. -1 = 'Empty' pointer; 0 = No more pointers The pointers in the CICONBLK pointer table can actually be zeroed in the file, since when loading with RSRC_LOAD they will automatically be filled in. (This is possible for the AES to do because the CICONBLKs are - in an RSC file - REQUIRED to follow directly on the pointer table.) The pointer table is ended with a -1. So the actual structure, to which the pointer in the second longword of the extension is pointing, looks like this in an RSC file on disk: L: 0 (= there is a first CICONBLK following this table) L: 0 (= there is a second CICONBLK following this table) ... etc. L: -1 = no more CICONBLKs 1st CICONBLK (and its related structures - se below) 2nd CICONBLK ( - " - ) OBJECTS ------- Each OBJECT is built as follows (12 words = 24 bytes): W: # of next sibling object (for last child: # of parent; for root: -1) W: # of first child object (or -1 for childless object) W: # of last child object (or -1 for childless object) W: Object type. B: 'Extended type', program definable. B: Basic object type (20-33) used by system - see below. W: Flags - bit 0: Selectable (in dialogue boxes; not used for menu items) 1: Default (= selectable through RETURN key) 2: EXIT (when left mouse button pressed and released) 3: Editable (Set in G_FTEXT and G_FBOXTEXT objects) 4: Radio button (= deselects sibling radio buttons) 5: Last object of tree (in object list). IMPORTANT BIT! 6: TOUCHEXIT (immediately when left mouse button pressed) 7: This and subordinate objects hidden/ignored (See below) 8: Further indirection of object spec. (Not normally used) TOS 4+ only: 9: 3D 'indicator' (for radio buttons & on/off buttons) 10: 3D 'activator' (for exit buttons) BOTH 9&10 set: 3D background obj. => any OUTLINE in 3D, plus if fill colour = white and fill pattern = 0 replace with default 3D BG colour. 11: Sub-menu attached (for menu items only) W: State - bit 0: Selected (Drawn in inverse video) 1: Crossed (with WHITE lines) 2: Checked (Check mark to the (upper) left) 3: Disabled (Any text written in light style) 4: Outlined ((Extra) border drawn around object) 5: Shadowed (down and right) L: Object specification: 1) Simple B: Character (for G_BOXCHAR - object type 27), or zero boxes B: Border thickness (0=none,positive=inwards,neg.=outw.) possibly N: Border colour with N: Text colour single N: Fill level (0-7) +8 for any opaque (white) char. backgr character N: Fill colour 2) For other objects the specification is a pointer to either a simple text string, or to a further structure with more data (see below). W: X coordinate relative to the parent W: Y coordinate relative to the parent W: Width W: Height -------- 12 words (24 bytes) OBJECT TYPES: Object spec Boxes: 20. G_BOX see above 25. G_IBOX Invisible see above 27. G_BOXCHAR With single character see above Simple text: 26. G_BUTTON Centred in box -> string 28. G_STRING -> string 32. G_TITLE Menu title -> string Formatted text: 21. G_TEXT -> TEDINFO 22. G_BOXTEXT In box -> TEDINFO 29. G_FTEXT Editable -> TEDINFO 30. G_FBOXTEXT Editable in box -> TEDINFO Images: 23. G_IMAGE -> BITBLK 31. G_ICON with mask + possib. text -> ICONBLK 33. G_CICON Ditto in colour (TOS 4) -> CICONBLK Special objects: 24. G_PROGDEF Freely definable -> APPLBLK If the indirection flag (bit 8 of flag word) is set, each of the above object specifications is replaced with A POINTER TO the respective specification. (This would for most objects mean a pointer to a pointer). The indirection flag seems however to be rarely used (and never in an RSC file). THE EXTENDED TYPE (the upper type byte) is free to be used for any program- defined purpose. For instance a justification code could be placed here, telling the program to justify (left, right, centre) an object (string) within its parent before use. Other codes could instruct the program to change the object into a G_PROGDEF. NOTE: Objects of type G_PROGDEF never occur in RSC files, but are always set up by the program. Typically the to-be-G_PROGDEF object is stored in the RSC file as one of the other standard type objects, but with the upper type byte (extended type) filled in as well - with some value that only the program will understand and use to translate the object into a G_PROGDEF with a suitable APPLBLK. This is the way that a program can have dialogue boxes with round buttons, or buttons that when selected become crossed instead of shown inverse video etc. EDITABLE OBJECTS - BUGS: Only in objects of types 29 or 30 (G_FTEXT and G_FBOXTEXT) should the editable flag (bit 3) normally be set. It tells the AES in what objects to place any cursor for text editing. Unfortunately there are (in earlier TOS versions) two bugs connected to this flag: 1) Editable last object: One bug shows itself when an editable object is also the last one in the object list for a tree (in which case the vitally important last-object bit should be set). The object won't be found by TOS when moving the cursor around with the keys. (TOS simply reads and acts upon the last-object bit before it does the editable bit.) The solution is however very simple. Just make sure the last object never is editable, if need be by adding a further little invisible box to the end of the list. 2) Editable hidden objects: The other bug makes itself known when an editable object is 'hidden' - i.e. when the 'hidden bit' (bit 7 in flag word) is set in either the object itself or in one of its 'ancestors'. The whole sub-tree, originating in an object with its hidden bit set, should be invisible and ignored, but TOS does NOT ignore editable objects when moving the cursor around with the keys. The idea (or at least one of them) behind hidden sub-trees is to make possible alternative sub-trees between which could be easily toggled without the need for a complete rearrangement of the tree. A cure for the bug proposed in ST Magazin (by Laurenz Prner in issue 6/92 of this now extinct German magazine) is a sub-routine that clears any editable bits in a hidden sub-tree, plus another routine that restores these bits when the sub-tree is made unhidden again. The objects on which to perform these manipulations should I think be exactly the objects of types 29 or 30. (Laurenz Prner proposes another method to mark objects to manipulate - making use of one of the currently unused flag bits - but this I think would be more prone to cause future incompatibility problems.) TREES ----- An object tree is stored as a list of object structures, and each object is identified by its order number in this list. The root object is always the first object and is assigned the number zero. The last object in the list must have its last-object flag set (bit 5 in flag word at offset 8 or in the BYTE at offset 9 from object start). MENUS: Whereas the construction of a dialogue box is restrained by very few rules, menu object trees always look like follows (I have used line graphics here - if you don't use a PC or Protext, use PC_LINES.PRG to get them right on screen): Ŀ Ŀ Ŀ Ĵ TITLE: G_TITLE MENU BAR: ACTIVE: G_IBOX G_BOX, but X=2, Y=0, H= Ŀ Ĵ no fill or Ĵ $301, W=enough Ĵ TITLE: G_TITLE border,X=0 for the titles, Y=0,H=$201 not more Ŀ Ĵ TITLE: G_TITLE Ŀ Ŀ MENU: Ĵ MENU ITEM: G_STRING I_BOX Ŀ Extent: Ĵ DROPBOX: G_BOX Ŀ Screen Border thick- Ĵ MENU ITEM: G_STRING Ĵ ness = -1, Big enough for Ŀ its menu items Ĵ MENU ITEM: G_STRING Ŀ DROP AREA: Ŀ Ŀ G_IBOX, DROPBOX: G_BOX Ĵ MENU ITEM: G_STRING Y=$301, Border thick- Ĵ Extent: Ĵ ness = -1, Ĵ Ŀ area below Big enough for Ĵ MENU ITEM: G_STRING menu bar its menu items Ŀ Ŀ Ĵ MENU ITEM: G_STRING DROPBOX: G_BOX Border thick- Ŀ Ĵ ness = -1, Ĵ MENU ITEM: G_STRING Big enough for its menu items Ŀ Ĵ MENU ITEM: G_STRING As can be seen the root of the MENU is a G_IBOX (invisible box) which should cover just about the whole screen (possibly leaving out the bottom lines). It has got two children: the menu bar (= top screen line) and the drop area (below this). As you can see the height of the menu bar should be '$201' (=1 full character plus two more pixels), while the height of its child is one pixel more (??? Don't ask me why, it's a complete mystery to me, but all menus look like this). Also the drop area doesn't begin until 1 character + 3 pixels down. The child of the menu bar, an invisible box called ACTIVE, defines the area within which the AES is to watch the mouse. It should be wide enough for all the titles but not more. First title should be the program name (in uppercase letters according to at least German standard I think). First dropbox should contain EIGHT menu items, the first of which is usually 'About ...', the second should be a disabled separator line ('---------' use as many hyphens as needed). The remaining six are dummy objects used by the AES for accessories, and are typically initialized to point to strings containing '1', '2' etc. up to '6' but I really don't think it matters what these strings contain. Each title should be preceded by a space and followed by another one. The strings used for menu items should each begin with two spaces (enough for a check mark + space). The selectable flag is NOT used in menu item objects (they are selectable anyway, as long as they aren't disabled). For a weird programmer, some very small changes to the above description are possible I think, without jeopardizing the function of the menu. For instance a second child could be added to the menu line, in the form of some symbol in the upper right corner of the screen. And for menu items could in some cases be used images instead of G_STRINGs. SECONDARY STRUCTURES -------------------- pointed to by the object specification pointer of some objects TEDINFO: (14 words = 28 bytes) L: Pointer to string (If 1st char. is '@' the rest interpreted as spaces) L: Pointer to 'template' (= String with underlines for editable positions) L: Pointer to 'validation string' (containing symbols for valid characters) W: Font: 3=sys(16/8 x8), 5=mini(6x6), 0,1,2=Speedo (0=prop,1=mono,2=bitmap) W: =6 (or font-ID with SpeedoGDOS and TOS 4) W: Justification (0=left, 1=right, 2=centred) W: Colour etc.: N: Border colour (Border and fill only used with N: Text colour G_BOXTEXT and G_FBOXTEXT) N: Fill level (0-7) +8 for any opaque (white) text backgr. N: Fill colour W: =0 (or font-size with SpeedoGDOS and TOS 4) W: Border thickness (0=none, positive=inwards, negative=outwards) W: String length incl. ending NULL byte W: Template length incl. ending NULL byte -------- 14 words (28 bytes) For G_TEXT and G_BOXTEXT (object types 21 and 22) only the first pointer is of interest. The other two pointers are used only with the editable objects G_FTEXT and G_FBOXTEXT (types 29 and 30). For these the text made visible is a combination of text template and string. Basically the template comes first followed by the string, whereby however any editable positions (underlines) in the template are treated as free gaps to fill with the characters from the string that are in turn. In the validation string each character corresponds to an editable position and defines what characters are allowed in this position. 9 must be a digit (0-9) A uppercase letter or space a uppercase or lowercase letter or space N digit, uppercase letter or space n digit, uppercase or lowercase letter or space F Any valid DOS pathname character plus : * ? P Any valid DOS pathname character plus \ : * ? p Any valid DOS pathname character plus \ : X Anything Note that (contrary to what is indicated in for instance the Atari compendium) the A, a, N and n options work just as well with 'non-English' letters in the Atari character set. (I.e. the AES is capable of recognizing letters in the extended character set as such and to distinguish between uppercase and lowercase versions of these.) However, this doesn't fully apply when F, P or p is used, to limit input to DOS characters. In this case AES will for A-Z accept only uppercase versions, but from the extended set will unfortunately accept ANY letter (e.g. even lowercase in spite of the fact that uppercase also exists in the set). On the other hand the DOS functions themselves are incapable of making out the difference between uppercase and lowercase for these. (I would recommend that any lowercase input is converted, when possible, into uppercase before being used in a GEMDOS call.) Interestingly the AES will, as DOS characters, NOT accept special characters such as '+' or '-' even though these, like the 'extended letters' are actually tolerated by the DOS functions. (The above notes apply to my own Swedish TOS, but I don't think this differs between TOS nationalities.) --------------- BITBLK: (7 words = 14 bytes, but see under ICONBLK) L: Pointer to image data W: Width in bytes (must be even) W: Height in pixels W: X-offset in pixels into image data (skip image part to the left of this) W: Y-offset in pixels into image data (skip image rows above) W: Colour (0-15) ------- 7 words (14 bytes) --------------- ICONBLK: (17 words = 34 bytes, but see below) (ALL measures and coordinates within ICONBLK are IN PIXELS.) L: Pointer to mask L: Pointer to image L: Pointer to icon text string N: Foreground colour N: Background colour B: Icon letter/character W: X of icon letter RELATIVE TO THE IMAGE W: Y - " - W: X of image RELATIVE TO THE OBJECT W: Y - " - W: Image width in pixels (must be divisible by 16) W: Image height i pixels W: X of icon text RELATIVE TO THE OBJECT W: Y - " - W: Width of icon text area W: Height - " - -------- 17 words (34 bytes) The lengths given for BITBLK and ICONBLK are those I have found in newer documentation (e.g. the Atari compendium) as well as in existing RSC files. But some older sources (Katherine Peel's Concise Atari ... Reference Guide and Abacus' GEM book) add one extra null word to the end of ICONBLK and one ditto to the end of BITBLK (the latter according to Peel only). This may have been an early specification now dropped, maybe anyone else knows more? In any case this probably means some caution is advised in assuming that the next ICONBLK will follow at offset 34 from the current one. However, in an RSC file on disk I think it can be safely assumed that if the longword at offset 34 is non-zero it IS the first longword (the image pointer) of the next ICONBLK. (Should it read zero it must consist of an ending null-word + only the upper word of the image pointer in the beginning if next ICONBLK.) And the similar thing, for offset 14, goes for BITBLKs. The argument is of course that, within a file less than 64 K in size, any longword address has its upper word zeroed. --------------- CICONBLK (TOS 4): (19 words or 38 bytes) 17W = ICONBLK (Here the length of 17 words should be certain ?) L: In an RSC file on disk: Number of CICONs associated with the icon When in use in memory : Pointer to the first CICON. -------- 19 words (38 bytes) CICON: (11 words = 22 bytes) W: Number of colour planes for this CICON L: Pointer to image for unselected icon L: Pointer to mask for unselected icon L: Pointer to image for selected icon (or 0 if "unselected image" used) L: Pointer to mask for selected icon (if image pointer wasn't 0) L: 0 = No more CICONs Otherwise pointer to next CICON (or =1 in a file) -------- 11 words (22 bytes) Colour images should match dimensions etc. given in (the part of CICONBLK that corresponds to an) ICONBLK. If the AES doesn't find a CICON with a usable number of colour planes, the mono icon defined by ICONBLK will be used. In an RSC file on disk the CICON pointers don't have to be right (since the data will follow immediately after the CICON anyway), but if a separate image is used for selected icons, this must at least be signalled by its image pointer being non-zero. In an RSC file on disk all structures and data for a colour icon must be in the following order: CICONBLK Mono data Mono mask 12 bytes for any icon text First CICON its data for unselected icon its mask for unselected icon ( its data for selected icon if corresponding CICON pointer non-zero) ( its mask for selected icon if above CICON data pointer was non-zero) Next CICON etc. Should you create colour icons not in an RSC file, you can ignore the above but need to make sure the pointers are right. --------------- APPLBLK: (2 longs or 8 bytes - though the last longword could be omitted) L: Pointer to sub-routine for drawing the object on screen L: Any data to be passed to the routine by the AES -------- 4 words (8 bytes) The sub-routine is called by the AES each time the object (or part of it) needs to be redrawn. It should call VDI routines only (no AES), the first of which should be VS_CLIP to set a clip rectangle according to coordinates received from the AES. Any VDI call could then be used, including VRO_CPYFM, so it would for instance be possible to copy an image block of pre-drawn fancy buttons and indicators. A VDI virtual workstation of your own should be used, NOT the physical screen workstation of the AES, even though the sub-routine will be used in the AES context. A pointer is passed as input from the AES and a word value is to be returned as output. The input is on the stack - i.e. a longword found at 4(SP) - and the return value is to be placed in D0 (like with other C routines on the ST). You are NOT free to use all registers as you like. Specifically I have established, in experiments, that change of contents in A5, A6 or D3 leads to visible bad results, but probably none of registers D3-D7 or A3-A6 should be touched. On the other hand registers D0-D2 and A0-A2 are documented as being used as work registers by the VDI functions your routine is to call and can probably be used directly in the routine as well. An addition there is VERY LITTLE ROOM ON STACK within the routine (a system stack is used here), which, among other things means that you cannot solve the previously mentioned problem by saving all registers on stack (though you can save them elsewhere of course). Some tests I have made seem to indicate that there is room for only about 6 or so longwords on the stack (with TOS 1.04). Remember that any sub-routine call will use up one of these. Maybe someone else has got more precise official documentation regarding use of registers and stack within the custom object drawing routines? The INPUT pointer - from 4(SP) - points to the structure PARMBLK: PARMBLK: (15 words, set up by the AES - don't worry about where) L: Pointer to tree W: Object number within tree W: Previous state (according to which the object shown on screen at entry) if this = Current state, then nothing is on screen at time of entry W: Current state (to be drawn by routine or left for the AES to do) W: X of object W: Y -"- W: Width -"- W: Height -"- W: X of clip rectangle to be set by routine W: Y - " - W: Width - " - W: Height - " - L: The program-defined data from the APPLBLK -------- 15 words (30 bytes) If Previous state = Current state, then the sub-routine must draw the complete object from scratch. If not, then the routine could possibly just add the necessary modifications to make the object image on screen reflect the new state. (Reasonably, the latter case could only occur for selectable objects, with the selected bit being toggled on and off.) As OUTPUT (in D0.W) should be returned a word containing the state bits that the routine leaves for the AES to draw. In most cases the current state word is simply read from offset 8 in PARMBLK and returned as it is. But the routine could also clear certain bits that it takes care of itself. For instance a program defined button could have a routine draw a cross in it when it is selected and clear the selected state bit from the return value to prevent the AES from inverting colours. NOTE 1: The AES, in my experience, doesn't care to draw the SHADOWED state for G_PROGDEFs, so any such effect must be drawn by the custom routine. NOTE 2: Only in connection to the initial drawing (the one where Previous state = Current state) the AES (of TOS 1.04) seems to draw ANYTHING AT ALL! This shouldn't make a difference for non-selectible objects, but a G_PROGDEF that is SELECTABLE probably must do any and all of the desired states itself (except for a possible outline, which shouldn't normally be affected by further redraws after the initial one, since it lies outside the actual object area). . 7f\.. "fRSC_TO_SBAS =]^RSC_TO_SPRG =u RSC_TO_STXT +J6DATA "RSC_TO_S -- Version 1.00 by Keith Baines 3rd January 1993" ' ' Program to Convert Wercs RSC and HRD files as assembler source code ' This version accepts command line file name and needs Basic 2 ' ' Install as Devpac tool with the following parameters: ' ' ' ' COMPILER OPTIONS ' ' L20 - Leave 20K for the file selector and alerts ' Y+ - Don't open a window ' R32 - Need big stack for recursive Quick Sort Routine ' REM $OPTION L20,Y+,R32 DEFINT A-Z LIBRARY "GEMAES","GEMVDI" CONST TRUE=-1 CONST FALSE=0 COMMON SHARED TAB$,DC_B$,DC_W$,DC_L$,EQU$ TAB$=CHR$(9) DC_B$=TAB$+"DC.B"+TAB$ DC_W$=TAB$+"DC.W"+TAB$ DC_L$=TAB$+"DC.L"+TAB$ EQU$=TAB$+"EQU"+TAB$ ' ' Object Types ' CONST G_BOX=20 CONST G_TEXT=21 CONST G_BOXTEXT=22 CONST G_IMAGE=23 CONST G_PROGDEF=24 CONST G_IBOX=25 CONST G_BUTTON=26 CONST G_BOXCHAR=27 CONST G_STRING=28 CONST G_FTEXT=29 CONST G_FBOXTEXT=30 CONST G_ICON=31 CONST G_TITLE=32 ' ' RSC file Header Entries ' CONST Version = 0 CONST First_Tree = 1 CONST First_TED = 2 CONST First_ICON = 3 CONST First_BITBLK= 4 CONST List_FStrs = 5 CONST First_String= 6 CONST First_IMG = 7 CONST List_FImgs = 8 CONST List_Trees = 9 CONST No_Objects = 10 CONST No_trees = 11 CONST No_TEDs = 12 CONST No_ICONs = 13 CONST No_BITBLKs = 14 CONST No_FStrs = 15 CONST No_FImgs = 16 CONST RSC_Length = 17 DIM X(11),Y(10) CALL Prepare CALL Dump NewLine NewLine Comment "==== End of File ====" PutString ";" CLOSE #1 PRINT GetAnyKey FALSE STOP -1 ' ' Get the input file name and load resource ' and header files. ' SUB Prepare STATIC P$,F$,F2$,dummy,T,N0$,P0$,CmdFlag IF COMMAND$<>"" THEN CmdFlag=TRUE F$=COMMAND$ IF RINSTR(F$,".")<=RINSTR(F$,"\") THEN F$=F$+".RSC" END IF ELSE CmdFlag=FALSE P$="A:\*.RSC" F$=FNGetFile$("Select Input File",P$,"") IF F$="" THEN STOP -1 END IF IF RIGHT$(F$,4)<>".RSC" THEN ErrorQuit "[ | File type must | be .RSC | ]" END IF F$=LEFT$(F$,LEN(F$)-3) IF NOT FEXISTS(F$+"RSC") THEN ErrorQuit "[ | File "+N0$+"RSC | not found | ]" END IF IF CmdFlag THEN F2$=F$+"RS" ELSE T=RINSTR(F$,"\") P0$=LEFT$(F$,T)+"*." N0$=MID$(F$,T+1) F2$=FNGetFile$("Enter Name of Output File",P0$+"RS",N0$+"RS") END IF IF F2$="" THEN STOP -1 IF FEXISTS(F2$) THEN T=RINSTR(F2$,"\") ContinueCheck "[ | Overwrite existing | file "+MID$(F2$,T+1)+" ? ]" END IF PrepOutput F2$ LoadResource F$+"RSC" Do_Header_File F$+"HRD" NewLine MainTitle "Resource data from file "+F$+"RSC" END SUB ' ' Read .HRD file, save tree, free string and free image names ' SUB Do_Header_File(FH$) SHARED TreeNames$(),FreeStrNames$(),FreeImgNames$() SHARED RSC_buffer() LOCAL VERS,LANG,TYPE,T_Index,O_Index,NAM$,dummy DIM TreeNames$(RSC_buffer(No_Trees)) DIM FreeStrNames$(RSC_buffer(No_FStrs)) DIM FreeImgNames$(RSC_buffer(No_FImgs)) IF NOT FEXISTS(FH$) THEN PRINT " ***" PRINT " *** No Header File" PRINT " ***" EXIT SUB END IF OPEN FH$ FOR INPUT AS #2 PRINT " ***" PRINT " *** Reading Header File: "+FH$ Get_Header VERS,0,LANG,0,0,0,0 IF VERS<>1 THEN PRINT " *** Wrong version number - not using file" PRINT " ***" EXIT SUB END IF IF LANG<>16 THEN PRINT " *** Not an assembly language header file - not used" PRINT " ***" EXIT SUB END IF PRINT " ***" DO GET_DATA TYPE,0,T_Index,O_Index,NAM$ SELECT CASE TYPE CASE 0,1 TreeNames$(T_Index)=NAM$ CASE 2,3 FreeStrNames$(T_Index)=NAM$ CASE 4 FreeImgNames$(T_Index)=NAM$ CASE 6 EXIT LOOP CASE ELSE : END SELECT LOOP NewLine CLOSE #2 END SUB ' ' Read header record from .HRD header file ' SUB Get_Header(V,N,L,S,C,Z,T) LOCAL T$ T$=INPUT$(8,#2) V=CVI(MID$(T$,1,2)) N=ASC(MID$(T$,3,1)) L=ASC(MID$(T$,4,1)) S=ASC(MID$(T$,5,1)) C=ASC(MID$(T$,6,1)) Z=ASC(MID$(T$,7,1)) T=ASC(MID$(T$,8,1)) END SUB ' ' Read data or EOF record from .HRD file ' SUB Get_Data(TYP,T,TI,OI,N$) LOCAL T$ T$=INPUT$(6,#2) TYP=ASC(MID$(T$,1,1)) T=ASC(MID$(T$,2,1)) TI=CVI(MID$(T$,3,2)) OI=CVI(MID$(T$,5,2)) N$="" IF TYP <>6 THEN T$=INPUT$(1,#2) DO UNTIL T$=CHR$(0) N$=N$+T$ T$=INPUT$(1,#2) LOOP END IF END SUB ' ' Open output file and write header information ' SUB PrepOutput(F$) STATIC T$ SHARED Display_Flag OPEN F$ FOR OUTPUT AS #1 Display_Flag = (1=form_alert(1,"[2][ | ECHO Output to Screen | ][ YES | NO ]")) RESTORE READ T$ WINDOW OPEN 2," "+T$+" ",PEEKW(SYSTAB+58),PEEKW(SYSTAB+60),PEEKW(SYSTAB+62),PEEKW(SYSTAB+64),1 CLS MOUSE -1 RESTORE READ T$ Comment T$ Comment "" Comment MID$(DATE$,4,3)+MID$(DATE$,1,3)+MID$(DATE$,7) NewLine PutDefn "G_BOX",20 PutDefn "G_TEXT",21 PutDefn "G_BOXTEXT",22 PutDefn "G_IMAGE",23 PutDefn "G_PROGDEF",24 PutDefn "G_IBOX",25 PutDefn "G_BUTTON",26 PutDefn "G_BOXCHAR",27 PutDefn "G_STRING",28 PutDefn "G_FTEXT",29 PutDefn "G_FBOXTEXT",30 PutDefn "G_ICON",31 PutDefn "G_TITLE",32 NewLine PutDefn "NONE",0 PutDefn "SELECTABLE",1 PutDefn "DEFAULT",2 PutDefn "EXIT",4 PutDefn "EDITABLE",8 PutDefn "RBUTTON",16 PutDefn "LASTOB",32 PutDefn "TOUCHEXIT",64 PutDefn "HIDDEN",128 PutDefn "INDIRECT",256 NewLine PutDefn "NORMAL",0 PutDefn "SELECTED",1 PutDefn "CROSSED",2 PutDefn "CHECKED",4 PutDefn "DISABLED",8 PutDefn "OUTLINED",16 PutDefn "SHADOWED",32 NewLine END SUB SUB PutDefn(C$,V) PutString C$+EQU$+FNNum$(V) NewLine END SUB ' ' Load Resource file into Buffer ' SUB LoadResource(F$) SHARED RSC_Buffer() STATIC T$,I,L,L& OPEN F$ FOR INPUT AS #2 T$=INPUT$(36,#2) CLOSE #2 L=CVI(MID$(T$,35,2))\2+1 IF 2&*L+10000&>FRE("") THEN ErrorQuit " File Too Large | To Load " END IF PRINT " ***" PRINT " *** Reading Resource File: "+F$ PRINT " ***" DIM RSC_Buffer(L) BLOAD F$,VARPTR(RSC_Buffer(0)) END SUB ' ' Outputs RSC data as assembler. ' SUB Dump STATIC I,T LOCAL Order(9,1) SHARED RSC_Buffer() ' Decide order for subroutines to dump file in correct order Order(0,0)=RSC_buffer(List_Trees) Order(1,0)=RSC_buffer(List_FStrs) Order(2,0)=RSC_buffer(List_FImgs) Order(3,0)=RSC_buffer(First_Tree) Order(4,0)=RSC_buffer(First_TED) Order(5,0)=RSC_buffer(First_BITBLK) Order(6,0)=RSC_buffer(First_ICON) Order(7,0)=RSC_buffer(First_String) Order(8,0)=RSC_buffer(First_IMG) Order(9,0)=RSC_buffer(RSC_Length) FOR I=0 TO 9 Order(I,1)=I NEXT I QSORT 10,1,Order() DumpHeader FOR I=0 TO 8 T=Order(I,1) SELECT CASE T CASE 0 ListTrees CASE 1 ListFreeStrings CASE 2 ListFreeImages CASE 3 DumpObjects CASE 4 DumpTED CASE 5 DumpBITBLK CASE 6 DumpICON CASE 7 DumpStrings CASE 8 DumpImages END SELECT NEXT I END SUB SUB DumpHeader SubTitle "RSC File Header" HeaderLine1 Version, "RSC File Version" HeaderLine2 First_Tree, "Start of Objects" HeaderLine2 First_TED, "Start of TEDINFOs" HeaderLine2 First_ICON, "Start of ICONBLKs" HeaderLine2 First_BITBLK, "Start of BITBLKs" HeaderLine2 List_FStrs, "List of free strings" HeaderLine2 First_String, "Start of string data" HeaderLine2 First_IMG, "Start of bit image data" HeaderLine2 List_FImgs, "List of free images" HeaderLine2 List_Trees, "List of Trees" HeaderLine1 No_Objects, "Number of Objects" HeaderLine1 No_trees, "Number of Trees" HeaderLine1 No_TEDs, "Number of TEDINFOs" HeaderLine1 No_ICONs, "Number of ICONBLKs" HeaderLine1 No_BITBLKs, "Number of BITBLKs" HeaderLine1 No_FStrs, "Number of Free STrings" HeaderLine1 No_FImgs, "Number of Free Images" HeaderLine2 RSC_Length, "RSC File Length" NewLine END SUB SUB HeaderLine1(I,T$) SHARED RSC_buffer() PutString DC_W$+FNNum$(RSC_Buffer(I))+TAB$+TAB$+T$ NewLine END SUB SUB HeaderLine2(I,T$) SHARED RSC_buffer() PutString DC_W$+FNHexNum$(RSC_Buffer(I))+TAB$+TAB$+T$ NewLine END SUB SUB ListTrees SHARED RSC_buffer(),TreeNames$() SubTitle "List Of Trees" DumpList RSC_buffer(List_Trees),TreeNames$(),RSC_buffer(No_trees),"tr_" END SUB SUB ListFreeStrings SHARED RSC_buffer(),FreeStrNames$() IF RSC_buffer(No_FStrs)>0 THEN SubTitle "List Of Free Strings" DumpList RSC_buffer(List_FStrs),FreeStrNames$(),RSC_buffer(No_FStrs),"fs_" END IF END SUB SUB ListFreeImages SHARED RSC_buffer(),FreeImgNames$() IF RSC_buffer(No_FImgs)>0 THEN SubTitle "List Of Free IMAGES" DumpList RSC_buffer(List_FImgs),FreeImgNames$(),RSC_buffer(No_FImgs),"fi_" END IF END SUB SUB DumpList(F,Nam$(1),N,Prefix$) SHARED RSC_buffer() STATIC I PutString "LIST_"+HEX$(F) IF N=0 THEN PutString DC_L$+"0" NewLine ELSE FOR I=0 TO N-1 PutString DC_L$+Prefix$ IF Nam$(I)="" THEN PutString FNNum$(I) ELSE PutString Nam$(I) END IF NewLine NEXT I END IF NewLine END SUB SUB DumpObjects SHARED RSC_buffer() SHARED TreeNames$() STATIC T,I,TYP,NEWTREE,F,N,OBJCOUNT,X F=RSC_buffer(First_Tree)\2 N=RSC_buffer(No_Objects) NEWTREE=TRUE OBJCOUNT=0 T=F SubTitle "Trees" FOR I=1 TO N IF NEWTREE THEN X=FNFindTree(T) IF X>=0 THEN IF TreeNames$(X)<>"" THEN PutString "tr_"+TreeNames$(X) ELSE PutString "tr_"+FNNum$(X) END IF NewLine END IF NEWTREE=FALSE ELSE PutString "; Object "+FNNum$(OBJCOUNT) NewLine END IF INCR OBJCOUNT TYP=RSC_buffer(T+3) PutString DC_W$ PutString FNNum$(RSC_buffer(T))+"," PutString FNNum$(RSC_buffer(T+1))+"," PutString FNNum$(RSC_buffer(T+2))+"," DumpType TYP PutString DC_W$+FNFlag$(RSC_buffer(T+4)) NewLine PutString DC_W$+FNStatus$(RSC_buffer(T+5)) NewLine SELECT CASE TYP CASE G_BOX,G_IBOX,G_BOXCHAR DumpWords T+6,2 CASE G_BUTTON,G_STRING,G_TITLE PutString DC_L$+"STR_"+HEX$(RSC_buffer(T+7)) NewLine CASE G_TEXT,G_BOXTEXT,G_FTEXT,G_FBOXTEXT PutString DC_L$+"TED_"+HEX$(RSC_buffer(T+7)) NewLine CASE G_IMAGE PutString DC_L$+"BITBLK_"+HEX$(RSC_buffer(T+7)) NewLine CASE G_ICON PutString DC_L$+"ICON_"+HEX$(RSC_buffer(T+7)) NewLine CASE G_PROGDEF PutString DC_L$+"0" NewLine CASE ELSE DumpWords T+6,2 END SELECT DumpWords T+8,4 IF RSC_buffer(T+4) AND &H20 THEN NEWTREE=TRUE OBJCOUNT=0 NewLine NewLine END IF T=T+12 NEXT I END SUB DEF FNFlag$(W) LOCAL T$ IF W=0 THEN FNFlag$="NONE" EXIT SUB ELSE T$="" IF W AND 1 THEN T$=T$+"+SELECTABLE" END IF IF W AND 2 THEN T$=T$+"+DEFAULT" END IF IF W AND 4 THEN T$=T$+"+EXIT" END IF IF W AND 8 THEN T$=T$+"+EDITABLE" END IF IF W AND 16 THEN T$=T$+"+RBUTTON" END IF IF W AND 32 THEN T$=T$+"+LASTOB" END IF IF W AND 64 THEN T$=T$+"+TOUCHEXIT" END IF IF W AND 128 THEN T$=T$+"+HIDDEN" END IF IF W AND 256 THEN T$=T$+"+INDIRECT" END IF IF W AND &HFE00 THEN T$=T$+"+"+FNHexNum$(W AND &HFE00) END IF FNFlag$=MID$(T$,2) END IF END DEF DEF FNStatus$(W) LOCAL T$ IF W=0 THEN FNStatus$="NORMAL" EXIT SUB ELSE T$="" IF W AND 1 THEN T$=T$+"+SELECTED" END IF IF W AND 2 THEN T$=T$+"+CROSSED" END IF IF W AND 4 THEN T$=T$+"+CHECKED" END IF IF W AND 8 THEN T$=T$+"+DISABLED" END IF IF W AND 16 THEN T$=T$+"+OUTLINED" END IF IF W AND 32 THEN T$=T$+"+SHADOWED" END IF IF W AND &HFFC0 THEN T$=T$+"+"+FNHexNum$(W AND &HFFC0) END IF FNStatus$=MID$(T$,2) END IF END DEF SUB DumpType(TYP) LOCAL T IF TYP AND &HFF00 THEN PutString FNHexNum$(TYP AND &HFF00)+"+" END IF T=TYP AND &HFF SELECT CASE T CASE G_BOX PutString "G_BOX" CASE G_TEXT PutString "G_TEXT" CASE G_BOXTEXT PutString "G_BOXTEXT" CASE G_IMAGE PutString "G_IMAGE" CASE G_PROGDEF PutString "G_PROGDEF"+TAB$+"*** ProgDef must be patched at run-time" NewLine PutString DC_W$ CASE G_IBOX PutString "G_IBOX" CASE G_BUTTON PutString "G_BUTTON" CASE G_BOXCHAR PutString "G_BOXCHAR" CASE G_STRING PutString "G_STRING" CASE G_FTEXT PutString "G_FTEXT" CASE G_FBOXTEXT PutString "G_FBOXTEXT" CASE G_ICON PutString "G_ICON" CASE G_TITLE PutString "G_TITLE" CASE ELSE PutString FNNum$(T)+TAB$+"*** ERROR - UNKNOWN TYPE ***" END SELECT NewLine END SUB SUB DumpTED SHARED RSC_buffer() STATIC I,T,N T=RSC_buffer(First_TED)\2 N=RSC_buffer(No_TEDs) IF N=0 THEN EXIT SUB SubTitle "TEDINFOs" FOR I=1 TO N PutString "TED_"+HEX$(2*T)+DC_L$ PutString "STR_"+HEX$(RSC_buffer(T+1))+"," PutString "STR_"+HEX$(RSC_buffer(T+3))+"," PutString "STR_"+HEX$(RSC_buffer(T+5)) NewLine DumpWords T+6,8 NewLine T=T+14 NEXT I NewLine END SUB SUB DumpBITBLK SHARED RSC_buffer(),FreeImgNames$() STATIC T,I,J,N,X T=RSC_buffer(First_BITBLK)\2 N=RSC_buffer(No_BITBLKs) IF N=0 THEN EXIT SUB SubTitle "BITBLKs" FOR I=1 TO N X=FNFindFreeImage(T) IF X>=0 THEN IF FreeImgNames$(X)<>"" THEN PutString "fi_"+FreeImgNames$(X) ELSE PutString "fi_"+FNNum$(X) END IF NewLine END IF PutString "BITBLK_"+HEX$(2*T)+DC_L$ PutString "IMG_"+HEX$(RSC_buffer(T+1)) NewLine DumpWords T+2,5 NewLine T=T+7 NEXT I NewLine END SUB SUB DumpICON SHARED RSC_buffer() STATIC T,I,J,N T=RSC_buffer(First_ICON)\2 N=RSC_buffer(No_ICONs) IF N=0 THEN EXIT SUB SubTitle "ICONBLKs" FOR I=1 TO N PutString "ICON_"+HEX$(2*T)+DC_L$ PutString "IMG_"+HEX$(RSC_buffer(T+1))+"," PutString "IMG_"+HEX$(RSC_buffer(T+3))+"," PutString "STR_"+HEX$(RSC_buffer(T+5)) NewLine DumpWords T+6,6 DumpWords T+12,5 NewLine T=T+17 NEXT I NewLine END SUB SUB DumpWords(F,N) SHARED RSC_buffer() STATIC J PutString DC_W$ FOR J=0 TO N-2 PutString FNHexNum$(RSC_buffer(F+J))+"," NEXT J PutString FNHexNum$(RSC_buffer(F+N-1)) NewLine END SUB SUB DumpStrings SHARED RSC_buffer() STATIC P&,Q&,L,Start,Finish Start=RSC_buffer(First_String)\2 Finish=RSC_buffer(First_IMG)\2 IF Start>=Finish THEN EXIT SUB SubTitle "String Data" P&=VARPTR(RSC_buffer(Start)) Q&=VARPTR(RSC_buffer(Finish)) L=2*Start WHILE P&=0 THEN NewLine IF FreeStrNames$(X)<>"" THEN PutString "fs_"+FreeStrNames$(X) ELSE PutString "fs_"+FNNum$(X) END IF ELSE PutString "STR_"+HEX$(L) END IF PutString DC_B$ FIRST=TRUE QUOTE=FALSE DO X=PEEKB(P&) IF X>=ASC(" ") AND X<>ASC("'") THEN IF NOT QUOTE THEN IF FIRST THEN PutString "'" FIRST=FALSE ELSE PutString ",'" END IF QUOTE=TRUE END IF PutString CHR$(X) ELSE IF QUOTE THEN PutString "'" QUOTE=FALSE END IF IF FIRST THEN FIRST=FALSE ELSE PutString "," END IF PutString FNNum$(X) END IF INCR P& INCR L LOOP UNTIL X=0 NewLine END SUB DEF FNFindTree(T) FNFindTree=FNFindInList(2*T,List_Trees,No_Trees) END DEF DEF FNFindFreeStr(P&) SHARED RSC_buffer() FNFindFreeStr=FNFindInList(P&-VARPTR(RSC_buffer(0)),List_FStrs,No_FStrs) END DEF DEF FNFindFreeImage(P) FNFindFreeImage=FNFindInList(2*P,List_FImgs,No_FImgs) END DEF DEF FNFindInList(D,First,No) SHARED RSC_buffer() STATIC I,T,N T=RSC_buffer(First)\2+1 N=RSC_buffer(No)-1 FOR I=0 TO N IF RSC_buffer(T)=D THEN FNFindInList=I EXIT DEF END IF T=T+2 NEXT I FNFindInList=-1 END DEF SUB DumpImages SHARED RSC_buffer() STATIC P,X,I,W,H,N,ImgList() STATIC FI,NI,FB,NB FI=RSC_buffer(First_ICON)\2 NI=RSC_buffer(No_ICONs) FB=RSC_buffer(First_BITBLK)\2 NB=RSC_buffer(No_BITBLKs) IF (NI=0) AND (NB=0) THEN EXIT SUB DIM ImgList(2*NI+NB-1,2) SubTitle "Bit Image Data" N=0 P=FI FOR I=1 TO NI ImgList(N,0)=RSC_buffer(P+1) ImgList(N,1)=RSC_buffer(P+11)\16 ImgList(N,2)=RSC_buffer(P+12) INCR N ImgList(N,0)=RSC_buffer(P+3) ImgList(N,1)=RSC_buffer(P+11)\16 ImgList(N,2)=RSC_buffer(P+12) INCR N P=P+17 NEXT I P=FB FOR I=1 TO NB ImgList(N,0)=RSC_buffer(P+1) ImgList(N,1)=RSC_buffer(P+2)\2 ImgList(N,2)=RSC_buffer(P+3) INCR N P=P+7 NEXT I QSORT N,2,ImgList() FOR I=0 TO N-1 DumpOneImage ImgList(I,0),ImgList(I,1),ImgList(I,2) NEXT I END SUB SUB DumpOneImage(P,W,H) SHARED RSC_buffer() STATIC T,I,J T=P\2 Comment STR$(W*16)+" Wide by"+STR$(H)+" High" PutString "IMG_"+HEX$(P) FOR I=1 TO H PutString DC_W$ FOR J=1 TO W-1 PutString FNHexNum$(RSC_buffer(T)) IF (J MOD 8 = 0) THEN NewLine PutString DC_W$ ELSE PutString "," END IF INCR T NEXT J PutString FNHexNum$(RSC_buffer(T)) INCR T NewLine NEXT I NewLine END SUB ' ' Get number as string without initial space ' DEF FNNum$(N) IF N<0 THEN FNNum$=STR$(N) ELSE FNNum$=MID$(STR$(N),2) END IF END DEF ' ' Get number as Hex string with leading $ sign ' DEF FNHexNum$(N) FNHexNum$="$"+HEX$(N) END DEF ' ' Use the file selector to get a file name ' and validate it. ' DEF FNGetFile$(BYVAL Prompt$,VARPTR path$,filename$) STATIC r,dummy,button dummy=form_alert(1,"[0][ | "+Prompt$+" | ][OK]") fsel_input path$,filename$,button FNGetFile$="" IF button=0 THEN EXIT DEF ELSEIF filename$="" OR LEFT$(filename$,1)="." THEN dummy=form_alert(1,"[3][ A file name is required ][OK]") EXIT DEF ELSEIF INSTR(filename$,"*")>0 OR INSTR(filename$,"?")>0 THEN dummy=form_alert(1,"[3][ Ambiguous file name not allowed ][OK]") EXIT DEF END IF r=RINSTR(path$,"\") IF r=0 THEN path$=path$+"\" r=LEN(path$) END IF FNGetFile$=left$(path$,r)+filename$ END DEF ' ' Output string to screen and/or file ' SUB PutString(S$) STATIC I,C$ SHARED Display_Flag PRINT #1,S$; IF Display_Flag THEN FOR I=1 TO LEN(S$) C$=MID$(S$,I,1) IF C$<>TAB$ THEN PRINT C$; ELSE PRINT SPACE$(12 - ((POS(0)-1) MOD 12)); END IF NEXT I END IF END SUB SUB MainTitle(T$) STATIC L L=LEN(T$)+6 NewLine NewLine PutString STRING$(L,"*") NewLine PutString "** "+T$+" **" NewLine PutString STRING$(L,"*") NewLine NewLine END SUB SUB SubTitle(T$) STATIC L L=LEN(T$)+2 NewLine NewLine PutString ";"+STRING$(L,"-")+";" NewLine PutString "; "+T$+" ;" NewLine PutString ";"+STRING$(L,"-")+";" NewLine NewLine END SUB SUB Comment(T$) PutString "; "+T$ NewLine END SUB SUB Warn(T$) SHARED Display_Flag PRINT #1 PRINT #1,"***" PRINT #1,"*** WARNING - "+T$+" ***" PRINT #1,"***" PRINT PRINT " ***" PRINT " *** WARNING - "+T$+" ***" PRINT " ***" IF Display_Flag THEN CALL GetAnyKey(TRUE) END SUB SUB NewLine SHARED Display_Flag PRINT #1, IF Display_Flag THEN PRINT IF INKEY$<>"" THEN WHILE INKEY$<>"":WEND GetAnyKey TRUE END IF END IF END SUB SUB GetAnyKey(F) STATIC K$ PRINT "=== Press any key to continue ==="; IF F THEN PRINT " ([Esc] to cancel.)"; LOCATE ,1 DO K$=INKEY$ LOOP UNTIL K$<>"" PRINT SPACE$(60); LOCATE ,1 IF F AND (K$=CHR$(27)) THEN Comment "Program Cancelled - File Incomplete" STOP -1 END IF END SUB SUB ErrorQuit(M$) LOCAL dummy dummy=form_alert(1,"[3]"+M$+"[ QUIT ]") STOP -1 END SUB SUB ContinueCheck(M$) IF form_alert(1,"[2]"+M$+"[ CONTINUE | CANCEL ]") <> 1 THEN STOP -1 END IF END SUB ' ' Recursive Quick Sort (Adapted from K & P S/W Tools in Pascal) ' for array of image addresses and sizes ' SUB QSORT(N,R,A(2)) CALL RQUICK(0,N-1,R,A()) END SUB 'QSORT SUB RQUICK(LO,HI,R,A(2)) LOCAL I,J,PIVOT IF LOI) AND FNCompare(A(J,0),A(PIVOT,0))>=0 DECR J WEND IF (I=J CALL Exchange(I,HI,R,A()) IF (I-LO) < (HI-J) THEN CALL RQUICK(LO,I-1,R,A()) CALL RQUICK(I+1,HI,R,A()) ELSE CALL RQUICK(I+1,HI,R,A()) CALL RQUICK(LO,I-1,R,A()) END IF END IF END SUB 'RQUICK DEF FNCompare(X,Y) IF X=Y THEN FNCompare=0 ELSEIF X<$<|(o*lNN"N~NOOB-J+| V`NqAx-~ NZNJA-Ax-N&DC.BN-Ax.NNJA-Ax-N&DC.WN-Ax.NNJA-Ax-N&DC.LN-Ax.NNJA-Ax-N&EQUN-Ax.NNJ~ =A-.<v!N~ =A-.<v!NN,NNNNNpNN&==== End of File ====rN&A./NLNOpNN&;rN&A./NINO~NNN-"N-N&*.NNJA-A->-"RGNNJA.pN-N&Enter Name of Output FilerN&.-A-N&RSNrN&.-A-N&RSNrN&.-A./.-A./.-A./.-NGNO,NJA-N&NJGg~NA.NJGg~-A-N&\NB;G"pNN&[ | Overwrite existing | file -A->-"RGNN-N& ? ]NrN&A./NONOHmN XOpNA-N&RSCNrN&A./N4NOpNA-N&HRDNrN&A./N,NONNpNN&Resource data from file -A.N-N&RSCNrN&A./NJNON\NuNTpN~ "m. G=A.6<N,~"m. G=A.6<N,~"m. G=A.6<N,.,NFGJGgZNN& ***NN<N.NN& ***NN, Gg\NN&+ *** Wrong version number - not using fileNN, GgfNN&5 *** Not an assembly language header file - not usedNNJGgA. G> Gf&=,A.6<N-A.NJ`NqA. G> GgA. G> Gf&=,A.6<N-A.NJ`XNqA. G> Gf&=,A.6<N-A.NJ`"NqA. G> Gf`Nq`Nq`NqNN~NNN\NuNTpNA-~-~NNJ-, A-~=~NN ^0-,A-~=~NN, ^0-,A-~=~NN, ^0-,A-~=~NN, ^0-,A-~=~NN, ^0-, A-~=~NN, ^0-,A-~=~NN, ^0NN\NuNTpNA-~-~NNJ-,A-~=~NN, ^0-,A-~=~NN, ^0-,A-~=~NN ^0-, A-~=~NN ^0-,N&NJ., G> Gg\A-~-~NNJA-~NZNJGf2-,-,A.NNJA-~-~NNJ`NqNN\NuNT-,~=~=><N.~pN=N&,[2][ | ECHO Output to Screen | ][ YES | NO ]rN&>?<=A ./>=N՜NONJ;GD+| VA-NNJ~=N& -A.N-N& N-AF.Nè-.<FNŽN G>=AF.Nè-.<FNŽN G>=AF.Nè-.<FNŽN G>=AF.Nè-.<GNŽN G>=~NN~N(+| VA-NNJHmNLXOpNN&rN&A./NLNOpNNF-~=~N-NF-~=~NN-NF-~NNrN&A./NLNONNpNN&G_BOXrN&?<A ./A./NNOpNN&G_TEXTrN&?<A ./A./NNOpNN& G_BOXTEXTrN&?<A ./A./NNOpNN&G_IMAGErN&?<A ./A./NNOpNN& G_PROGDEFrN&?<A ./A./NNOpNN&G_IBOXrN&?<A ./A./NNOpNN&G_BUTTONrN&?<A ./A./NNOpNN& G_BOXCHARrN&?<A ./A./NNOpNN&G_STRINGrN&?<A ./A./NNOpNN&G_FTEXTrN&?<A ./A./NNOpNN& G_FBOXTEXTrN&?<A ./A./NNOpNN&G_ICONrN&?<A ./A./NNOpNN&G_TITLErN&?< A ./A./NNONNpNN&NONErN&?<A ./A./NNOpNN& SELECTABLErN&?<A ./A./NNOpNN&DEFAULTrN&?<A ./A./NNOpNN&EXITrN&?<A ./A./NNOpNN&EDITABLErN&?<A ./A./NNOpNN&RBUTTONrN&?<A ./A./NNOpNN&LASTOBrN&?< A ./A./NNOpNN& TOUCHEXITrN&?<@A ./A./NNOpNN&HIDDENrN&?<A ./A./NNOpNN&INDIRECTrN&?<A ./A./NNONNpNN&NORMALrN&?<A ./A./NNOpNN&SELECTEDrN&?<A ./A./NNOpNN&CROSSEDrN&?<A ./A./NNOpNN&CHECKEDrN&?<A ./A./NNOpNN&DISABLEDrN&?<A ./A./NNOpNN&OUTLINEDrN&?<A ./A./NNOpNN&SHADOWEDrN&?< A ./A./NNONNN\NuNTpN-, A.N-., G>?.-NFTONrN&A./NINONNN\NuNT-,~=~=><N.A-~$-~NNJ~NA-~#=~NN=~NRG;G<~->- ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~=~=A.v"N-~"m. G> ^0~ =~=A.v"N-~"m. G> ^0;|&=-&~=A.v"N->-& ^0Rm& m &o?< ?<A./A./A./NPdON;|&=-&~=A.v"N>;G,Jm,fN `Nq m,fN!`Nq m,fN"8`Nq m,fN$^`lNq m,fN4>`VNq m,fN6l`@Nq m,fN8`*Nq m,fN< `Nq m,fNARm& m&oNN\NuNTpNN&RSC File HeaderrN&A./NKNOpN?<N&RSC File Versionr N&A./A./NNOpN?<N&Start of Objectsr N&A./A./N tNOpN?<N&Start of TEDINFOsr N&A./A./N tNOpN?<N&Start of ICONBLKsr N&A./A./N tNOpN?<N&Start of BITBLKsr N&A./A./N tNOpN?<N&List of free stringsr N&A./A./N tNOpN?<N&Start of string datar N&A./A./N tNOpN?<N&Start of bit image datar N&A./A./N tNOpN?<N&List of free imagesr N&A./A./N tNOpN?< N& List of Treesr N&A./A./N tNOpN?< N&Number of Objectsr N&A./A./NNOpN?< N&Number of Treesr N&A./A./NNOpN?< N&Number of TEDINFOsr N&A./A./NNOpN?< N&Number of ICONBLKsr N&A./A./NNOpN?<N&Number of BITBLKsr N&A./A./NNOpN?<N&Number of Free STringsr N&A./A./NNOpN?<N&Number of Free Imagesr N&A./A./NNOpN?<N&RSC File Lengthr N&A./A./N tNONNN\NuNTpNA.-., G>"m. G>?.-NFTON-Ax.N-Ax.N-.,NrN&A./NINONNN\NuNTpNA.-., G>"m. G>?.-NGTTON-Ax.N-Ax.N-.,NrN&A./NINONNN\NuNTpNN& List Of TreesrN&A./NKNOpNN&tr_rN&~ "m. /Hm~ "m. /A./N"NO N\NuNT~"m. G=~NBJGgpNN&List Of Free StringsrN&A./NKNOpNN&fs_rN&~"m. /Hm~"m. /A./N"NO N\NuNT~"m. G=~NBJGgpNN&List Of Free IMAGESrN&A./NKNOpNN&fi_rN&~"m. /Hm~"m. /A./N"NO N\NuNTpNN&LIST_-., G>NnNrN&A./NINO., G>JGfBpNA-N&0NrN&A./NINONN`Nq~=., G=~DG^=~AN`NqpNA-.,NrN&A./NINO=-.,6<N-N&NJGg6pN?-NFTOrN&A./NINO`Nq=-.,6<N/NIXONNAN`PNqNNN\NuNT~"m. G=~N;GR~ "m. G>;GT;|PBmV;mRFpNN&TreesrN&A./NKNO~==-T~AHN`.NqJmPg?-FN?TO;GX=-X~N:JGg=-XA.6<N-N&NJGgLpNN&tr_-=-XA.6<NNrN&A./NINO`BNqpNN&tr_?-X-NFTONrN&A./NINONNBmP`NNqpNN& ; Object ?-V-NFTONrN&A./NINONNRmV>-FVG"m. G>;GNHmNIXOpN>-F"m. G>?NFTO-N&,NrN&A./NINOpN>-FRG"m. G>?NFTO-N&,NrN&A./NINOpN>-FTG"m. G>?NFTO-N&,NrN&A./NINOHmNN/fXOpNA.->-FXG"m. G>?.-N+TONrN&A./NINONNpNA.->-FZG"m. G>?.-N-TONrN&A./NINONN>-N Gg>-N Gg mNf.>-F\G??<A./A./N;O `fNq>-N Gg>-N Gg m NfdpNA-N&STR_N->-F^G"m. G>NnNrN&A./NINONN`Nq>-N Gg$>-N Gg>-N Gg mNfdpNA-N&TED_N->-F^G"m. G>NnNrN&A./NINONN`RNq mNfhpNA-N&BITBLK_N->-F^G"m. G>NnNrN&A./NINONN`Nq mNffpNA-N&ICON_N->-F^G"m. G>NnNrN&A./NINONN`tNq mNfBpNA-N&0NrN&A./NINONN`*Nq>-F\G??<A./A./N;O >-FG??<A./A./N;O >-FXG"m. G=~ ^JGg;|PBmVNNNN>-FG ;GFAHN`NqN\NuNTpN>,JGf"A-N&NONENJ`6Nq`0NqA-N&NJ=,~^JGg(A-A-N& +SELECTABLENNJ=,~^JGg$A-A-N&+DEFAULTNNJ=,~^JGg"A-A-N&+EXITNNJ=,~^JGg&A-A-N& +EDITABLENNJ=,~^JGg$A-A-N&+RBUTTONNNJ=,~ ^JGg$A-A-N&+LASTOBNNJ=,~@^JGg&A-A-N& +TOUCHEXITNNJ=,><^JGg$A-A-N&+HIDDENNNJ=,><^JGg&A-A-N& +INDIRECTNNJ=,><^JGg<^?.-NGTTONNJA-A-~NNJA.N NN\NuNTpN>,JGf$A-N&NORMALNJ`Nq`NqA-N&NJ=,~^JGg&A-A-N& +SELECTEDNNJ=,~^JGg$A-A-N&+CROSSEDNNJ=,~^JGg$A-A-N&+CHECKEDNNJ=,~^JGg&A-A-N& +DISABLEDNNJ=,~^JGg&A-A-N& +OUTLINEDNNJ=,~ ^JGg&A-A-N& +SHADOWEDNNJ=,~^JGg:A-A-N&+N-=,~^?.-NGTTONNJA-A-~NNJA.N NN\NuNT., G=><^JGgJpN., G=><^?NGTTO-N&+NrN&A./NINO., G=><^9GA. G> Gf6pNN&G_BOXrN&A./NINO`NqA. G> Gf6pNN&G_TEXTrN&A./NINO`NqA. G> Gf:pNN& G_BOXTEXTrN&A./NINO`NqA. G> Gf8pNN&G_IMAGErN&A./NINO`BNqA. G> GfpNN& G_PROGDEF-Ax.N-N&'*** ProgDef must be patched at run-timeNrN&A./NINONNHmNIXO`NqA. G> Gf6pNN&G_IBOXrN&A./NINO``NqA. G> Gf8pNN&G_BUTTONrN&A./NINO`NqA. G> Gf:pNN& G_BOXCHARrN&A./NINO`NqA. G> Gf8pNN&G_STRINGrN&A./NINO`NqA. G> Gf8pNN&G_FTEXTrN&A./NINO`>NqA. G> Gf:pNN& G_FBOXTEXTrN&A./NINO`NqA. G> Gf6pNN&G_ICONrN&A./NINO`NqA. G> G f8pNN&G_TITLErN&A./NINO`fNqpN?,NFTO-Ax.N-N&*** ERROR - UNKNOWN TYPE ***NrN&A./NINONNN\NuNT~"m. G=~N;G`~ "m. G>;GbJmbf`NqpNN&TEDINFOsrN&A./NKNO~==-b~AZN`NqpNN&TED_-~=>-`NnN-A.NrN&A./NINOpNN&STR_->-`RG"m. G>NnN-N&,NrN&A./NINOpNN&STR_->-`VG"m. G>NnN-N&,NrN&A./NINOpNN&STR_->-`ZG"m. G>NnNrN&A./NINONN>-`\G??<A./A./N;O NN>-`G;G`AZN`lNqNNN\NuNT~"m. G=~N;Gd~"m. G>;GnJmnf` NqpNN&BITBLKsrN&A./NKNO~==-n~AfN`Nq?-dN@>TO;Gp=-p~N:JGg=-pA.6<N-N&NJGgLpNN&fi_-=-pA.6<NNrN&A./NINO`BNqpNN&fi_?-p-NFTONrN&A./NINONNpNN&BITBLK_-~=>-dNnN-A.NrN&A./NINOpNN&IMG_->-dRG"m. G>NnNrN&A./NINONN>-dTG??<A./A./N;O NN>-d^G;GdAfN`LNqNNN\NuNT~"m. G=~N;Gr~ "m. G>;G|Jm|f`NqpNN&ICONBLKsrN&A./NKNO~==-|~AtN`NqpNN&ICON_-~=>-rNnN-A.NrN&A./NINOpNN&IMG_->-rRG"m. G>NnN-N&,NrN&A./NINOpNN&IMG_->-rVG"m. G>NnN-N&,NrN&A./NINOpNN&STR_->-rZG"m. G>NnNrN&A./NINONN>-r\G??<A./A./N;O >-rG ??<A./A./N;O NN>-rG;GrAtN`BNqNNN\NuNTHmNIXO~=., G=~DG^=~AN`hNqpN., G>m"m. G>?NGTTO-N&,NrN&A./NINOAN`NqpN., G=., G>^=~DG^"m. G>?NGTTOrN&A./NINONNN\NuNT~"m. G=~N;G~"m. G=~N;G=->-N:JGg`NqpNN& String DatarN&A./NKNO>-"m. +G~>-"m. +G~=>-;G--~.-NJGgHm~HmNNnNrN&A./NINOHmNIXO;|Bm., G. G~;G=-N& N,N:==-N&'N,NR^JGg>-FGJGgnJmg6pNN&'rN&A./NINOBm`.NqpNN&,'rN&A./NINO;|pN>-NZrN&A./NINO`NqJmg0pNN&'rN&A./NINOBmJmg Bm`.NqpNN&,rN&A./NINOpN?-NFTOrN&A./NINO., GR., GRPJmf^NNN\NuNT~=>,??< ?< N@j\O9G>,N\NuNT.,-~"m. Dޞ??<?<N@j\O9G>,N\NuNT~=>,??<?<N@j\O9G>,N\NuNT>, "m. G=~NRG;G>,"m. G=~DG^;G~==-~AN`LNq>-"m. G=>, NJJGg>-9G`"Nq>-TG;GAN`Nq~9G>,N\NuNT~"m. G=~N;G~ "m. G>;G~"m. G=~N;G~"m. G>;G=-~NJ==-~NJ^JGg`BNq~=>-m=~DG^=~=A.v"N,pNN&Bit Image DatarN&A./NKNOBm;m~==-~AN`ZNq=-~=A.v"N->-RG"m. G> ^0=-~=A.v"N->-G "m. G=~N ^0=-~=A.v"N->-G "m. G> ^0Rm=-~=A.v"N->-VG"m. G> ^0=-~=A.v"N->-G "m. G=~N ^0=-~=A.v"N->-G "m. G> ^0Rm>-G;GAN`Nq;m~==-~AN`Nq=-~=A.v"N->-RG"m. G> ^0=-~=A.v"N->-TG"m. G=~N ^0=-~=A.v"N->-VG"m. G> ^0Rm>-^G;GAN`RNq?<HmA./HmNPdO~==-~DG^=~AN`^Nq=-~=A.v"N/=-~=A.v"N/=-~=A.v"N/NDO AN`NqN\NuNT., G=~N;GpN., G>GN-N& Wide byN-., G>NN-N& HighNrN&A./NLNOpNN&IMG_-., G>NnNrN&A./NINO~=., G=~AN`0NqHmNIXO~=., G=~DG^=~AN`NqpN>-"m. G>?NGTTOrN&A./NINO=-~NJGfNNHmNIXO`.NqpNN&,rN&A./NINORmAN``NqpN>-"m. G>?NGTTOrN&A./NINORmNNAN`NqNNN\NuNTpN=,~N`JGgA->,NNJ`NqA->,N-~NNJA.N NN\NuNTpNA-N&$->,NnNNJA.N NN\NuNTpNpNN&[0][ | -.,N-N& | ][OK]NrN&?<A ./N՜NO;G6., /.,/Hm8N$O A-N&NJJm8f`Nq`4Nq-,N&N=-,~=~N-N&.N^JGg^pNN&"[3][ A file name is required ][OK]rN&?<A ./N՜NO;G6`Nq`Nq~--,N&*N-~Nt=~--,N&?N-~Nt^JGg`pNN&*[3][ Ambiguous file name not allowed ][OK]rN&?<A ./N՜NO;G6`lNq~--, N&\NB;G4Jm4f&-, -, N&\NNJ., NJ;G4A--, ~=>-4N-.,NNJA.N NN\NuNT~N.,NJmDg~=.,NJ=~A.N`NqA--,=-.~NNJA-Ax.NJGgNA.N`4NqN~ =~N=~DG^=~ NDG^H-~ N.NA.N`NqN\NuNT.,NJ-~ޞ;GBNNNNpN>-BH-N&*N,N.rN&A./NINONNpNN&** -.,N-N& **NrN&A./NINONNpN>-BH-N&*N,N.rN&A./NINONNNNN\NuNT.,NJ-~ޞ;GNNNNpNN&;->-H-N&-N,N.N-N&;NrN&A./NINONNpNN&; -.,N-N& ;NrN&A./NINONNpNN&;->-H-N&-N,N.N-N&;NrN&A./NINONNNNN\NuNTpNN&; -.,NrN&A./NINONNN\NuNT~NN<~NN&***NN<~NN&*** WARNING - -.,N-N& ***NNN<~NN&***NNJGg$NN& ([Esc] to cancel.)N~=~=~NA-NNNJA-N&NJGgN~<-~ N.N~=~=~N., G=A-~NZN^JGgTpNN&#Program Cancelled - File IncompleterN&A./NLNO~NN\NuNTpNN&[3]-.,N-N&[ QUIT ]NrN&?<A ./N՜NO9G~NN\NuNTpNN&[2]-.,N-N&[ CONTINUE | CANCEL ]NrN&?<A ./N՜NO Gg~NN\NuNT?<., G=~DG^?A./A./., /.,/NPON\NuNT., G=., G>N`JGgN., G>9G., G>9G>,9G=,>,N`==,~=.,v"N>?>==,~=.,v"N>?>=NSXO=~NX^JGgA. GRP`Nq=,>,NB==,~=.,v"N>?>==,~=.,v"N>?>=NSXO=~N:^JGgA. GSP`Nq=,>,N`JGg(A./A./., /.,/NSbO=,>,N:JGgA./.,/., /.,/NSbO=,., G>DG^=., G=>,DG^N`JGgd=,~DG^?.,/A./., /.,/NPO>,RG?A./.,/., /.,/NPO``Nq>,RG?A./.,/., /.,/NPO=,~DG^?.,/A./., /.,/NPON\NuNT=, >,NJJGg~9G`(Nq=, >,N`JGg~9G` Nq~9G>,N\NuNT~=., G=~AN`DNq., G==-.,v"N-., G==-.,v"NNtAN`NqN\Nu~N O/ C0<HЈ",Іd ,`Ь ЬЬ/// Bg?<JNAO Jf$EA8,Hp!!!.H HĐ"MBQd+@A+H+L|SE;EC@+I DX;D Nu _NT+K)O)N/NHz+_Nuv`Compiled with HiSoft BASIC Some code: Copyright (C) HiSoft 1987Hz?< NAa>?$SQdQd+FL+_/حd(Nut`t`t`t`B HNu"g"A//aRLfBNu"( H瀂H /?<HNA\OLLJgӭ"@Ҁ#A"g ,A",,_Nu" ,_Nu )H / ?<INA\OLNu\\agPB NudH0^@"g\"A")gN&Akd k L Nug0&J$Sjb jg#J L Nu&#K L Nu"Qf6@Lag" A E$#J BP )K5@ЊL NuH0&P$See&J`&"JL f B` dH^@&jbG $K&SebAfk&S2*Ag2"3@$C" L %AgL NuCg~L NuA5@$C`p"g$/ "ArJg$i 2*gЁ$R`"Qf$_Nup"g&/ "AJg$i 2*g $RAd0`"Qf$_Nu1AC!IC8$I g"@`"f!IBC< g(d"@PI`!@g$@%H E ^("haSoRgSe(g`x"Dp!a.Nua ^H ^JkgacR--`.Nuap`a 4 G("hahψaN h`&E` (aLa..Nua  *-JcaJgfSge*RIpH@U!Wgj`8JkH`RI VfdLR`LSj` *-Jca\JgbS/ Ř*kSpH@d4d0SWfJkH` VLgSj`ˉ`SWgd`JkH` Vf dL`LSj`haafXgXSeTaaVf>d`8<ae <{dNuafgSetHB Vfd`z`z Ga ^.`aJfgSetHB Vf֘d`ac`ad`a e`ab`aSe Se gNuJNu G&$P(<Ƅ VȐ"hNu Ga:("P~gNua Ga.Nuxa .Nu Gaxa.-NuRGfHx?<HNA\.Nu("hNu/ a _`/ @Jf(Sa. _` >HǏNu0HiH@>Nu H@g(Jg$HAJ@gJAfHA`JAgH@HAJAf ށeNu~NuDNu$JnDJnDai JkJjDNu^_HNu^]HNu^WHNu^fNu^\HNu^^HNu~Nu_HNu]HNuWHNufNu\HNu^HNu~Nu Ga4)k2BHҁC. Nuv!`6FBH*6HÖk$n UDk>HǞkn"aևQ"`J` GaJgF<fbxCifn4)C k2BCHUDk0BAҀQ8. NuJ-JgaP HxCSDt =QaL`a3a a a a FBH*&k趙nUDk.kھn"a6ևQ"` GaJgN<fxCift4)C kJ^f2BCHUDkJ^f0BAҀQ8. NuHxCSDt -QaL` GJf@4-H/ x$Nr6ASAHDJDfHD0Bk"R@Q/ pgaC2 AA ?H؁af222"8B2HR2 UAk0B22R@Q2prHASCd2`"Qd&_Nu2 4BSAA M BC p0 !Q&_ GJft4-H/ H/~$Nz6ESE"k\RaQ( pgaC2 APA?H؁a222FY"."R: UEk"""RalQ(*`2 Ga g()f<faXB`ZNu2) ~` ^Jf$M!J44BZ05@HR$  pgC5Cr g(`>Nu ^JfN$M!J444 %@R$  pgC5Cr.a| ` ^"G002Nu ^"G  "Nu ^"GL !iHNu?aJg0if$Ip0Nuar alHx a".( a ɉ G` Jj|-DA0tpve RdҘ`JjP0Q0Nu;B@'d HGBGHGx a&v tpxz`HGBGHGxavtpxzfJfJCfPŲ< e^0HRQ.`HGBGHGx axpJg$އeQJU0RއQ.`0x`xv,a$X@a0:0H@?a":0@@`a`./ ?NAT6"_NuH@B@H@ aH@0Nux v*aH@a-0@a-0X@@P|de 20`19`02000@fDAm\Nu20k @1APn Nu@1APl\Nu20k@Nv`@Nv`AX$- pQa m*g $ a E P`NuPSBegN`a(Qa$ a a *(a*(aFAa("a(`""_xza*a&Jk "-g$ANa>Ga(`*j(*`Aua(xa*dJg mNJg/a(^`AX/a(R"a(~JUgBJRga!L`H?8(H o(aLLf0ApNupDNuH/2a tf*0@"<g < g8H0 lNL LJNu"`r,HA,t,HB,"Lp`JgB<fp@`JfrAp`CB gB,>4>>>>ZQ"aapNuP"pNur,#pNupDNuB,B,#rarE`pNu|dp,@dAB#A B H`rarYaL8a`/ Jikrarba"W2)a2A at"WJikrafrca`"_2)aA `PXpNu?AA,0-F|gS@gA 00Nu    H @rareaLx*&I/ alfh&_< gR< gL<g<g< eغgHR/ at&_`JgSJ,gS,/ rar ara&_`a"p@Nu?,?<NMX<f lf-OfrpNupMNu?r aFr ax2< gp< g4R,,g,e lf< d<g<d<dp`0,<f< f ,#dR,#A???<NM\Jlg pNuB,`Rgp9Nu0,??<NMXrJfrpNuSgSgUgvSgpDNu&fvRBg/?<=NAJkbPOx(؃AaX)C)C$)C 8?<?B?<BNA)@BoNAO |||9Dp)@@)@NuPH`t9+@TlAgf LNuF5LCKKBg/?<NAXOapNu`8,<d$ g A @N.V@(e|dA`^vp<gJf",g a@farrpNur)ApNuHl(/, ??<?NAO Jk)@gB$Nup>Nua ,$e agp>)@NuA(r0R<gR$pNua",g$S)AS$dBg?/?<BNAO JkavpNua|pDNuat?(,$gA($e(٬٬$a8Jg*)l$/ /??<?NA"/O Jk&ѬfHpNua < g4< g2R,,g(,d"B O | ApB,aXNuB,I? OpaTNuJg$//??<@NA$/O "kpgp=Nup"Nua  IJ,f6,Sev H x ggRQdAL0`r`Jg, I,< g< gReSf C`^v`SH @vapAaFLgNu fXp6Nufp6XNuaB$BSt QdBg?/?<BNA"/O f Hl(/,??<?NAO Jkgr)ApNuaB$Bg?/?<BNA"/O Jl |fZ/?<?B?<BNAO "Jk@g(*HEJEgpH@ @anf(SE`HE  |aZf ,B$A(aHfNu` f(Jj", )A R $,Ga"G ,A(NuXp6Nu?<?B?<BNAO /?<?B?<BNAO "&Bg?/?<BNAff$, `$/`$,"O pNua Tp6Nu< g,< gR,,g,e&,$d 8(R$pNuB,`?r ar a2`p2Nu",pNuPRT:AUX:MID:KBD:LST:H@b0HCXAaؤg (IpLNuLp4Nu8<0DaQNuH@af Hp0a L LJNu lN -f ?+|pha.+m+mNuBm;| Ap 0Q0BmpNBT@g$pMa;@p-PT@;@0<da0-f:pMa;@;|0<da0-faJCannot open workstation;@;@Bn+mxrBmBmp&ad;mjp;@;@m;@lBm;|Bm;|p'a2Bm;|;|paBm;|BmpaNuQQa -gBm+@paBBm;|pzaa(a pa`:BmBmpeaNupD`p4ONu/ H0@a^0/|bCXHI o f"+|phaA`C1111/HC2"n"rpda`kx6A(a9@;@@|B,B,#9m "o B2B.Jo g& IaFa R/ a )H."_HaB"l.;l;|+IpiaRmA+HpiaL 8A0P0000pea;|phaL@ASBSCHH`J-Qfaxp0,lj@C$"T#zT3zRa*aah|aa"A p f ;lN;|Va0Dp@#@")@(_NuHaF2R,;|;|HA+A ,Ь+@NAX~Nu GEXa/ ?<ANA\J@k`HmX?<ANA\J@g|fHNu Gaha< gEXa AX `lJgS` / ?<NAT$_2A:BRA?/ ?<GNAPONu GaJgD<f8pC2)@m$gA0)HjF-SAfa -Nuan ah ab 0. n0.n"G"]e,0.n e2;@0HR@;@42-F3A;A80.ne2TI;@2ҁb+I,ED5555EDB44vAC,a+mx+H+I;C;|;|pma`"^48LRD60;@8VЀb0A, H20AnaA( GJf 09`@oNu|eYGG0G~(NuHz?<&NN\ONu| d:|dBmBm|gp|aG0;p>5Nu0<a>-xNuxa^gR/?<HNA\OJgf @Nu/?<INA\Jga*3a$ m| h,NuaJGk SGgJfJf `| GaXa,Sjaaa` I2  g (=WJANuaak/ /(f ;gl`SgbRUaf((/ag^erHa2L$_ /g`( R/`$@a8Bga`Ba:a"_ m|!I,NuRUatPOf/a< _` _$_< fp=QNu _$_aB"Ha6/a/Bg?<JNAO NuJfa"JfJfÈЁЄTNuJfgf`NuajaJfJg.Jg*"HVJAj<=g LJf`LNupNu$m|x*<gCxafna$fbCxafVJfJf  fRH`(JfJf/(Uax.$_6SCkfr aQNu<ae <{dNuE/ a>$_  fRSfx&/ SeaQ"_.`pr<:d <0e.@0`"<Gd <Ae@7`<gd<ae @WҀ`NuARGV_PBPa0|JgD0A4</va`. Compiled with HiSoft BASIC 0<rtvaf;H>;HDA0<rtvafNuaDLST:4H>a<6 Vaݾ"0 I0a޶f ^`ݮv` naݜSfXv<Igv<Ogv<Rgv<Af2HCt4>.a6 Va\"0HC IaTf ^aLT ^`Dat6v`ٜapaކf4 fJ-g prap` f?. i a؇Ea0_`0|aSH0fNu<!g.<fH/"h*)g/ I<!fa PHSf _*Nu"hmlH(x"mpaLNuJGo |d0GNu0Gp4`ؔ;mD>Nua;G>Nu;|>Nu GaV$0m>pa G`Hp0m>aHA86HBrBDHAJAgHAJCgAcVB8SDpr aQNuJGo8SD0m>`JGkg.p0m>a~gJBgHGJGgHBRBBdBSGHzCt0m>p`J -gB`&x?<?<NMXJ@f xa.Nu/<NMXJ@g$-Og <faנM?xa.0NuxH@?a.0BNu:RGf:-D(a˴.0E$p`p$"^0^`p`g,/ m fAN _`?/<NM\Nu/<NMXNuCX/ aB _`/a2? Va0 I"tva۾f8p*aJp"_$a@a ^`ڢx0DpafRD|ca־C>Nuv`//a? Val0 I"tvabfp"_$aad ^`L0mDpaHB~BNu0|`0mDpa~ANup0mD`42 -PfARmja>SmjNupJGk gSGf4pa2g,jpaHB2RAJVjTNNuSA4j?pa42p`SBdaJgDJ-f>|g8|Mg2|Ng,|Og&X;D;mf;U/-BP+LaNuafNuX`z~|f~`a"Ava*aL<f~af??<LNAALa 0( AXBSaA4`AWa _aJUgAYar2a`~q Y Program terminated - press a key to exit pFATAL ERROR:  at line ,GgT klknf`@,k^g>k^g2k8<d,&B<ރeNuRid~S<Nu.NuJNu<oD&B<ރeNukgvgPk<<d&B꫞kBSbHGއ[kgNu~Nu<oD&.<`DgjD`k gBAkjDNu<Nu~NuBkjDDNufDJjNuHz_Jg0jzDi&SbHGއ[Jj dRNuMC68343 FLOATING POINT FIRMWARE (C) COPYRIGHT 1981 BY MOTOROLA INC.HF~Jg*]lDvUt QCt壌JgGNuF"&g0pC2DC|m |4n"Q,gJlDk.Nu~DNu~Nup"DjDG@AB|Ab DSGkUĆQ(k(j2xc 0D@`fc 0D@`D@@`D@@ԆӅdf UĆR@(fJk0<NuJkJkxAS@J[NuJkV`LHaLHav$OaO `HВR@,@L0 : 8 HB.ڄGHGH@EHE>߃8 < : HBކ,CޅCބC8HFHB: ؅C: ؅C: HA؅CHFHGDHGHDDHFCHF8*܄C8 ܄CHA8 ܄C86 HA؃vHECHEHFDHFHDDHECHE8ڄ"$&@H@@ JmS@|fJf JfS@LXNuLH,.ahLHa\v$OaO `LD@؀ D(*H8x8pJff f0`&e0USjpRSfҎg0L JkS@|fJf JfS@Nua8 g0HGOGG,H@O@@HGBG p̀Nu|Nuއg HGNu gV H@H@@mH|lFJj@H@VHFNFr FJgdH@Ri H@.<H@Nu~NuDNu/ aJEk6|d WAPd"HpEaf( "_NupaZD+r2a`DE" 4E|d ICP$ISESA Q<0Q.`paD-| d0` oE c I`S@f.RNuvgv I0<0QJf JfzvRNuH(I??<HA|aVe*AaNeAlaVo`A^a8ezAa@Ro`Aba$dABaBo`A,adPAa,So`LfNuLLaTHNuLLHaLHNuGLLaap0JgaLaLaHQKA$a~eJjPO:6"LLNu?@$@Y@@@È@jA.AcAׄAe B_B7HvBm@B0Bּ&4C k7CAyؠCv4WnUF؉<ҲMC68341 FLOATING POINT FIRMWARE (C) COPYRIGHT 1981 BY MOTOROLA INC.xa/ Jj|-a&R _.aNuxaψ  Ira("H GaNu,<@Jj<ahNuai 0GgDNuH?@.N"Op QOLNu(j (u@#< k:9/6ɿ2_2/ ,*(ԥ%C@"k( $P@  =nѷYŬG7ֿwp_Aܯ ٌ .BҴ$5ϐ|敔ȸw2œJ~ePm%tѸv z Jgkzއ[ Fnz Fml8DD/A p@<&0@ W.:BEHExHCڃHG6ڃdR:HEXOޅkއSFdRFv 8fi FgNu~NuJDk~HFJ<Nu~|ag*e -WHFag .fagnS<NuafVafdRFa~g .fatg Eg<ef0adge -WaTf8aNf DbE`JjDDDSN .fa(faeSF`&eeeևe ևeօe.NuzgLSg< g< g< g< g` +g -g 0e 9bDNuDNuDNuO// ?<JH>f~A?|+."jT/"<HFvAbPmn d\HSC`]HRCPng`\HSCPmg/|E+00,4jDBT/- B eR/.B //tC$JjJ(fB8X*x UeBޅ D0Q>HL|TNuHFo GbAp 5dSGfxA/|E+00|+ `d/ .gR :fR0`RR~1HGHG Ef (+VfR :f$ R ` h01f|+S /f S _ Eg0`LNuH.f 0tLNu,av"OE` 5e / ".gR :f0`|1RG$_ Eg0`<-gr |RE "0V|ddJFgJGn0D@F|dP` |>|dD`(.0D@S@e0`0S@fO$LNu0S@f0Gc.S@f`SFg .SFfSGE+00JGlDGT(| eR(G `pq+o"/`hpp"/a^ o Nup.A0 0 "o00+oa o0NurLA p`.A+PC2BYpa^p/`|r+A p`r*Ap+P`p`pz`p{`pr` p2`p`p);o+oa8>Nup`p(`p` p `p!`p-+o`r'` rs`r`rN;o`r `r /H@+@+o`JApapGa`(r0`rFApaa$`p,;o +oaժA r`pOa՚`LpMaՒ`Dp6+oaՄ`6A paPplat`&Apa@pad`p;o"/HA+AaLAr`&AEp"`4Qph"/`C2QNu>E"`2QNuA@p a+o /H@+@parA`A pCapK+o `8re`rdAp`$ri`rHAp`rI`rJAp`r3Apat0aԖ>Nupo`p`pj`p`p"/HA+A`p5` pk`pg`pf;o`pm`A+HAX+HpxaCXx oalE o`,Apa+o+opy`vA+H Pa^p}ad o$o( f` rP`r| oa o`rQ` rn`r4;o oaa`AX+HSe`BNuaAX+H` oa+o ;op"`  _HaexA+Haz[aL/ N oBPNuzZ oadp d(a o aRAfap0ag o0 o Efa oEX"JJf( S`& oapdpxa$Se `Bp a>Nu+o`p7A00 0+oa o0 o0Nup8;o ;o+o a> o0NuCX I /$H@""/HA"C6420aC Q0 a0 a0 a0Nur#;o oaD m|Aa^`r;oAX+Hpa> o0 o0 o `;o0<a`+o +o /H@+@p$`+o+o +o+op%`;o ;o /H@+@p&`6*(,06T$"Z2:><>:<><<>:<0><8<<:>:<2<<<<<<2 .JDDFFDHHLHBFDFFFJJD:<:<6PbPbPHBR:&p2v*,($$,$,$20Dzb`<((4bf$RFJH @HJHHJFHJpdZZL" p*v*HL" pfZZL"( P$N^ `*D408@&":,nl>.H *<2>2 N:N4 h"jV6\,20 RSC_TO_S.PRG can be installed in the Devpac 3 Tools menu and called when the file in the top window has the same filename as the resource file (e.g. if resource is TEST.RSC, top window should contain TEST.S) Use the following settings for the Tools menu entry: Command Line: As shown Directory: Tool's Save Files: Yes Path: D:\DEVPAC3\TOOLS\RSC_TO_S.PRG (or wherever it is) Command: %? Pause on Return: No Report all errors: No Run as TOS: No RUN as GEM: Yes . Gf.. TXT_DISP HfSPRITES Kf. Hf.. FfBANKTEXTBAS *rLionpoulos$$L A simple text displayer using bank 15 to store the text by J.DavidsonSTETV Q$(d):ͦ:::$:q,wF>Z:Q:SASTST4(>Z:,>Z:TQ$(A):A42,,,,,D<,,,,,:q,_ Scroll-routined2A$:S(nS:jxSHQSTQ::,:TQ$(Q)::dvSPQETQ::,:TQ$(Q)::ddϠTР:F$(*.*,Select a file to view):F$:Ѡ:2Ҡ#,F$:J(#):#(Ӡ,J:zLF$,"ڴ:Preparing text......0>Z:(8P()2A$((()8P)):2A$( )*X$TQ$(>Z):"X(*X$):TQ$(>Z)(O):(TQ$(>Z),"X)*X$:>Z:8P:$.TQ$(>Z)TQ$(>Z)2A$ 8P . Kf.. FfSPRITE1 BAS !jNLionpoulos H >> Routine to draw a fast sprites (Try doing this with screen$!)  >> Coded by Steven Jordan,( >> Copyright Diamond Software 19932 >> Move the mouse...<$Fͦ::ަ:::::P > We need 16 images - 1 because we'll start at zero^ZHEIGHT;:WIDTH ::,(HEIGHT)0d > Array for the pos of the stored imagesn%Y_POS(),x > Get the picture palette and draw it()," > Shift the sprite 16 times . :::0 > Set-up the screens and limit the mouse<:,?WIDTH,HEIGHTN ----------------------------------------------------------------------NȊ | Main Loop |NҊ ----------------------------------------------------------------------܊ - > Clear the screen ::MX:MY8: > Get the offset off 16 : get sprite to display4:XMX:IMG(MX)(": > Draw the sprite on the screenv,: ,(),,%Y_POS(IMG),WIDTH,%Y_POS(IMG)HEIGHT,,X,MY*6: > Swap the address of the screens @::J T::^Nh ----------------------------------------------------------------------Nr | Pre-Shift the graphics |N| ----------------------------------------------------------------------B > Move the picture left because I did'nt draw at X cood 1!^.I:,,,WIDTH,HEIGHT,,:IY  Scroll the image 16 times 0XD: > Scroll the image one pixel to the right for the next image<:,,,WIDTH,HEIGHT,,,: > Store the image into a memory bankL: ,,,,WIDTH,HEIGHT,(),,YJ: > Remember where the image is stored and add height to get new one>:%Y_POS(X)Y:YYHEIGHT X "3  @>P> >6 ;p9ps8=}?xπ,7p?xa#n!~?p8@ %@ޠ#~_r8@ /@@&yp8@\,P@X ([wp8@~)@( wxp8@h8+@!~8p8@R'@ӿ#x|@p8@.@?Ϣ Ϝ?0~p8@9;(@ ;  ;?p8`@!`@ ?=xp8`!`!=~p8` h'` h#|p8`߀.P`ǀ&Pyp8`7}/`ïȵy*?uxp8`@>(W'`@<(U-P~?}ۺz ?x8` W/h=p-` ?W/(&oyq|yÇp8a)/m`,Pa?pu (6xw Çp8c)c舉( w}0wxÇxp8g qp+gyp!G.{~8燏|Þp8n w+'nos;ʁ+# ǖ$<}|>ýp8oT.n#W'ȁ .o.?|ûp8oʱ9(ngm.9 lq=ýp8g>`Pp!gPp ;mxÞp8g9!g|9?ȁx!W}vǰ~ ׋8`>~|w` so=x<`N?KʁP< ot ~<iÿ=8`yȅ`ܟȁhemny<ß8`>(ȅ?a;Ͼ+.ȅt>6ݱ.x<Ïx8a@ȭPb5u~@ȅJ@y÷8bQP0ȁe_6ȁu_6x 8` OoyN{x8 8`@@?xr?@><x 8h"@ w~p9͠>~p9͞x, 2~b@(}giϐ }giϏw_D0p8f@?D( D'xBI8h  ϡ|W ~|!W ~|@H?8x  ǧ|G ~#įG ~P2H?8` 2Pv_ʰ&P_ʱ@ D5O8`0 1/jpzC0pzC0uxB!8`z@'gtwz@-Ptwz@z9A8`@h-Eh(Ehwx@8?186xs}s`{L91w0cw>c  Lj ??;#a000000 #b000000 #c7770007000600070055200505552220770557075055507703111103 #d #E 18 12 #W 00 00 02 07 4B 10 08 A:\*.*@ #W 00 00 02 0B 4C 09 00 @ #W 00 00 0A 0F 34 09 00 @ #W 00 00 0E 01 34 09 00 @ #M 03 00 00 FF A ICTARI_MAG@ @ #M 00 01 00 FF B FLOPPY DISK@ @ #T 00 03 02 FF TRASH@ @ #F FF 04 @ *.*@ #D FF 01 @ *.*@ #G 03 FF *.APP@ @ #G 03 FF *.PRG@ @ #P 03 FF *.TTP@ @ #F 03 04 *.TOS@ @