1*5825Srrh /* 2*5825Srrh * Copyright (c) 1982 Regents of the University of California 3*5825Srrh */ 4*5825Srrh #ifndef lint 5*5825Srrh static char sccsid[] = "@(#)asjxxx.c 4.6 02/14/82"; 6*5825Srrh #endif not lint 7*5825Srrh 8595Sbill #include <stdio.h> 9595Sbill #include "as.h" 10595Sbill #include "assyms.h" 11595Sbill 12636Shenry #define JBR 0x11 13636Shenry #define BRW 0x31 14636Shenry #define JMP 0x17 15595Sbill 16595Sbill /* 17595Sbill * The number of bytes to add if the jxxx must be "exploded" 18595Sbill * into the long form 19595Sbill */ 20636Shenry #define JBRDELTA 1 /* brb <byte> ==> brw <byte> <byte> */ 21636Shenry #define JXXXDELTA 3 /* brb <byte> ==> brb <byte> brw <byte> <byte> */ 22636Shenry #define JBRJDELTA d124 /* brb <byte> ==> jmp L^(pc) <byte>*d124 */ 23636Shenry #define JXXXJDELTA d124+2 /* brb <byte> ==> brb <byte> jmp L^(pc) <byte>*d124 */ 24595Sbill 25636Shenry int jbrfsize = JBRDELTA; 26636Shenry int jxxxfsize = JXXXDELTA; 27636Shenry 28595Sbill /* 29595Sbill * These variables are filled by asscan.c with the 30595Sbill * last name encountered (a pointer buried in the intermediate file), 31595Sbill * and the last jxxx symbol table entry encountered. 32595Sbill */ 33595Sbill struct symtab *lastnam; 34595Sbill struct symtab *lastjxxx; 35595Sbill 36636Shenry initijxxx() 37636Shenry { 38636Shenry jbrfsize = jxxxJUMP ? JBRJDELTA : JBRDELTA; 39636Shenry jxxxfsize = jxxxJUMP ? JXXXJDELTA : JXXXDELTA; 40636Shenry /* 41636Shenry * Note: ifjxxxJUMP is set, then we do NOT do any tunnelling; 42636Shenry * this was too complicated to figure out, and in the first 43636Shenry * version of the assembler, tunnelling proved to be the hardest 44636Shenry * to get to work! 45636Shenry */ 46636Shenry } 47595Sbill /* 48595Sbill * Handle jxxx instructions 49595Sbill */ 50*5825Srrh ijxout(opcode, ap, nact) 51*5825Srrh struct Opcode opcode; 52*5825Srrh struct arg *ap; 53*5825Srrh int nact; 54595Sbill { 55595Sbill if (passno == 1){ 56595Sbill /* 57595Sbill * READ THIS BEFORE LOOKING AT jxxxfix() 58595Sbill * 59595Sbill * Record the jxxx in a special symbol table entry 60595Sbill */ 61595Sbill register struct symtab *jumpfrom; 62595Sbill 63595Sbill /* 64595Sbill * We assume the MINIMAL length 65595Sbill */ 66*5825Srrh putins(opcode, ap, nact); 67595Sbill jumpfrom = lastjxxx; 68630Shenry jumpfrom->s_tag = JXACTIVE; 69630Shenry jumpfrom->s_jxbump = 0; 70*5825Srrh if (opcode.Op_popcode == JBR) 71636Shenry jumpfrom->s_jxfear = jbrfsize; 72595Sbill else 73636Shenry jumpfrom->s_jxfear = jxxxfsize; 74595Sbill if (lastnam == 0) 75595Sbill yyerror("jxxx destination not a label"); 76630Shenry jumpfrom->s_dest = lastnam; 77630Shenry jumpfrom->s_type = dotp->e_xtype; /*only TEXT or DATA*/ 78630Shenry jumpfrom->s_index = dotp-usedot; 79595Sbill /* 80595Sbill * value ALWAYS (ALWAYS!!!) indexes the next instruction 81595Sbill * after the jump, even in the jump must be exploded 82595Sbill * (bumped) 83595Sbill */ 84630Shenry jumpfrom->s_value = dotp->e_xvalue; 85595Sbill njxxx++; 86595Sbill } else {/* pass2, resolve */ 87595Sbill /* 88595Sbill * READ THIS AFTER LOOKING AT jxxxfix() 89595Sbill */ 90*5825Srrh reg long oxvalue; 91*5825Srrh reg struct exp *xp; 92*5825Srrh reg struct symtab *tunnel; 93*5825Srrh reg struct arg *aplast; 94*5825Srrh struct Opcode nopcode; 95595Sbill 96595Sbill aplast = ap + nact - 1; 97630Shenry xp = aplast->a_xp; 98630Shenry if (lastjxxx->s_tag == JXTUNNEL){ 99630Shenry lastjxxx->s_tag = JXINACTIVE; 100630Shenry tunnel = lastjxxx->s_dest; 101630Shenry xp->e_xvalue = tunnel->s_value /*index of instruction following*/ 102595Sbill - 3 /* size of brw + word*/ 103636Shenry + ( ( (tunnel->s_jxfear == jbrfsize) && 104630Shenry (tunnel->s_jxbump == 0))?1:0); 105595Sbill /*non bumped branch byteis only 2 back*/ 106595Sbill } 107630Shenry if (lastjxxx->s_jxbump == 0){ /*wasn't bumped, so is short form*/ 108*5825Srrh putins(opcode, ap, nact); 109595Sbill } else { 110*5825Srrh if (opcode.Op_popcode != JBR){ 111*5825Srrh /* 112*5825Srrh * branch reverse conditional byte over 113*5825Srrh * branch unconditional word 114*5825Srrh */ 115630Shenry oxvalue = xp->e_xvalue; 116630Shenry xp->e_xvalue = lastjxxx->s_value; 117*5825Srrh nopcode = opcode; 118*5825Srrh nopcode.Op_popcode ^= 1; 119*5825Srrh putins(nopcode, ap, nact); 120630Shenry xp->e_xvalue = oxvalue; 121595Sbill } 122*5825Srrh nopcode.Op_eopcode = CORE; 123*5825Srrh nopcode.Op_popcode = jxxxJUMP ? JMP : BRW; 124*5825Srrh putins(nopcode, aplast, 1); 125595Sbill } 126595Sbill } 127*5825Srrh } 128595Sbill 129595Sbill jalign(xp, sp) 130595Sbill register struct exp *xp; 131595Sbill register struct symtab *sp; 132595Sbill { 133595Sbill register int mask; 134680Shenry /* 135680Shenry * Problem with .align 136680Shenry * 137680Shenry * When the loader constructs an executable file from 138680Shenry * a number of objects, it effectively concatnates 139680Shenry * together all of the text segments from all objects, 140680Shenry * and then all of the data segments. 141680Shenry * 142680Shenry * If we do an align by a large value, we can align 143680Shenry * within the a.out this assembly produces, but 144680Shenry * after the loader concatnates, the alignment can't 145680Shenry * be guaranteed if the objects preceding this one 146680Shenry * in the load are also aligned to the same size. 147680Shenry * 148680Shenry * Currently, the loader guarantees full word alignment. 149680Shenry * So, ridiculous aligns are caught here and converted 150680Shenry * to a .align 2, if possible. 151680Shenry */ 152*5825Srrh if ( ( (xp->e_xtype & XTYPE) != XABS) 153678Shenry || (xp->e_xvalue < 0) 154678Shenry || (xp->e_xvalue > 16) 155678Shenry ) { 156595Sbill yyerror("Illegal `align' argument"); 157595Sbill return; 158595Sbill } 159680Shenry if (xp->e_xvalue > 2){ 160680Shenry if (passno == 1){ 161*5825Srrh yywarning(".align %d is NOT preserved by the loader", 162*5825Srrh xp->e_xvalue); 163*5825Srrh yywarning(".align %d converted to .align 2", 164*5825Srrh xp->e_xvalue); 165680Shenry } 166680Shenry xp->e_xvalue = 2; 167678Shenry } 168595Sbill flushfield(NBPW/4); 169595Sbill if (passno == 1) { 170630Shenry sp->s_tag = JXALIGN; 171630Shenry sp->s_jxfear = (1 << xp->e_xvalue) - 1; 172630Shenry sp->s_type = dotp->e_xtype; 173630Shenry sp->s_index = dotp-usedot; 174595Sbill /* 175595Sbill * We guess that the align will take up at least one 176595Sbill * byte in the code output. We will correct for this 177595Sbill * initial high guess when we explode (bump) aligns 178595Sbill * when we fix the jxxxes. We must do this guess 179595Sbill * so that the symbol table is sorted correctly 180595Sbill * and labels declared to fall before the align 181595Sbill * really get their, instead of guessing zero size 182595Sbill * and have the label (incorrectly) fall after the jxxx. 183595Sbill * This is a quirk of our requirement that indices into 184595Sbill * the code stream point to the next byte following 185595Sbill * the logical entry in the symbol table 186595Sbill */ 187630Shenry dotp->e_xvalue += 1; 188630Shenry sp->s_value = dotp->e_xvalue; 189595Sbill njxxx++; 190595Sbill } else { 191630Shenry mask = (1 << xp->e_xvalue) - 1; 192*5825Srrh while (dotp->e_xvalue & mask) 193*5825Srrh Outb(0); 194595Sbill } 195595Sbill } 196595Sbill 197595Sbill /* 198595Sbill * Pass 1.5, resolve jxxx instructions and .align in .text 199595Sbill */ 200595Sbill jxxxfix() 201595Sbill { 202595Sbill register struct symtab *jumpfrom; 203595Sbill struct symtab **cojumpfrom, *ubjumpfrom; 204595Sbill register struct symtab *dest; 205595Sbill register struct symtab *intdest; /*intermediate dest*/ 206595Sbill register struct symtab **cointdest, *ubintdest; 207595Sbill 208595Sbill register struct symtab *tunnel; 209595Sbill int displ,nchange; 210595Sbill int badjxalign; /*if jump across an align*/ 211595Sbill int stillactives; /*if still active jxxxes*/ 212595Sbill int segno; /*current segment number*/ 213595Sbill int topono; /*which iteration in the topo sort*/ 214595Sbill register unsigned char tag; 215595Sbill /* 216595Sbill * consider each segment in turn... 217595Sbill */ 218595Sbill for (segno = 0; segno < NLOC + NLOC; segno++){ 219595Sbill badjxalign = 0; /*done on a per segment basis*/ 220595Sbill /* 221595Sbill * Do a lazy topological sort. 222595Sbill */ 223595Sbill for (topono = 1, nchange = 1; nchange != 0; topono++){ 224*5825Srrh #ifdef lint 225*5825Srrh topno = topno; 226*5825Srrh #endif lint 227595Sbill #ifdef DEBUG 228595Sbill if (debug) 229595Sbill printf("\nSegment %d, topo iteration %d\n", 230595Sbill segno, topono); 231595Sbill #endif 232595Sbill nchange = 0; 233595Sbill stillactives = 0; 234595Sbill /* 235595Sbill * We keep track of one possible tunnel location. 236595Sbill * A tunnel will eventually be an unconditional 237595Sbill * branch to the same place that another jxxx 238595Sbill * will want to branch to. We will turn a 239595Sbill * branch conditional/unconditional (word) that would 240595Sbill * have to get bumped because its destination is too 241595Sbill * far away, into a branch conditional/unconditional 242595Sbill * byte to the tunnel branch conditional/unconditional. 243595Sbill * Of course, the tunnel must branch to the same place 244595Sbill * as we want to go. 245595Sbill */ 246595Sbill tunnel = 0; /*initially, no tunnel*/ 247595Sbill SEGITERATE(segno, 0, 0, cojumpfrom, jumpfrom, ubjumpfrom, ++){ 248630Shenry tag = jumpfrom->s_tag; 249595Sbill if (tag <= IGNOREBOUND) 250595Sbill continue; /*just an ordinary symbol*/ 251595Sbill if (tag == JXALIGN){ 252595Sbill tunnel = 0; /*avoid tunneling across a flex alocation*/ 253595Sbill continue; /*we take care of these later*/ 254595Sbill } 255636Shenry if ( jumpfrom->s_jxfear == jbrfsize /*unconditional*/ 256595Sbill || ( tag == JXINACTIVE /*inactive bumped*/ 257630Shenry && (jumpfrom->s_jxbump != 0) 258595Sbill ) 259595Sbill ) tunnel = jumpfrom; 260595Sbill if (tag != JXACTIVE) 261595Sbill continue; 262630Shenry dest = jumpfrom->s_dest; 263630Shenry if (jumpfrom->s_index != dest->s_index){ 264595Sbill yyerror("Intersegment jxxx"); 265595Sbill continue; 266595Sbill } 267630Shenry displ = dest->s_value - jumpfrom->s_value; 268595Sbill if (displ < MINBYTE || displ > MAXBYTE) { 269595Sbill /* 270595Sbill * This is an immediate lose! 271595Sbill * 272595Sbill * We first attempt to tunnel 273595Sbill * by finding an intervening jump that 274595Sbill * has the same destination. 275595Sbill * The tunnel is always the first preceeding 276595Sbill * jxxx instruction, so the displacement 277595Sbill * to the tunnel is less than zero, and 278595Sbill * its relative position will be unaffected 279595Sbill * by future jxxx expansions. 280636Shenry * 281636Shenry * No tunnels if doing jumps... 282595Sbill */ 283636Shenry if ( (!jxxxJUMP) 284636Shenry && (jumpfrom->s_jxfear > jbrfsize) 285595Sbill && (tunnel) 286630Shenry && (tunnel->s_dest == jumpfrom->s_dest) 287630Shenry && (tunnel->s_index == jumpfrom->s_index) 288630Shenry && (tunnel->s_value - jumpfrom->s_value >= 289636Shenry MINBYTE + jxxxfsize) 290595Sbill ) { 291595Sbill /* 292595Sbill * tunnelling is OK 293595Sbill */ 294630Shenry jumpfrom->s_dest = tunnel; 295595Sbill /* 296595Sbill * no bumping needed, this 297595Sbill * is now effectively inactive 298595Sbill * but must be remembered 299595Sbill */ 300630Shenry jumpfrom->s_tag = JXTUNNEL; 301595Sbill #ifdef DEBUG 302595Sbill if(debug) 303595Sbill printf("Tunnel from %s from line %d\n", 304630Shenry jumpfrom->s_name, lineno); 305595Sbill #endif 306595Sbill continue; 307595Sbill } else { /*tunneling not possible*/ 308595Sbill /* 309595Sbill * since this will be turned 310595Sbill * into a bumped jump, we can 311595Sbill * use the unconditional jump 312595Sbill * as a tunnel 313595Sbill */ 314595Sbill tunnel = jumpfrom; 315630Shenry jumpfrom->s_tag = JXNOTYET; 316595Sbill ++nchange; 317595Sbill continue; 318595Sbill } 319595Sbill } /*end of immediate lose*/ 320595Sbill /* 321595Sbill * Do a forward search for an intervening jxxx 322595Sbill */ 323595Sbill if (displ >= 0) { 324595Sbill SEGITERATE(segno, cojumpfrom + 1,0,cointdest, 325595Sbill intdest, ubintdest, ++){ 326630Shenry if (intdest->s_value > dest->s_value) 327595Sbill break; /* beyond destination */ 328630Shenry if (intdest->s_tag <= JXQUESTIONABLE) 329595Sbill continue; /*frozen solid*/ 330630Shenry if (intdest->s_tag == JXALIGN){ 331630Shenry jumpfrom->s_jxoveralign = 1; 332595Sbill badjxalign++; 333595Sbill } 334595Sbill /* 335595Sbill * we assume the worst case 336595Sbill * for unfrozen jxxxxes 337595Sbill */ 338630Shenry displ += intdest->s_jxfear; 339595Sbill } 340595Sbill if (displ <= MAXBYTE){ 341595Sbill /* 342595Sbill * the worst possible conditions 343595Sbill * can't hurt us, so forget about 344595Sbill * this jump 345595Sbill */ 346630Shenry jumpfrom->s_tag = JXINACTIVE; 347595Sbill } else { 348595Sbill stillactives++; 349595Sbill } 350595Sbill } else { 351595Sbill /* 352595Sbill * backward search for intervening jxxx 353595Sbill */ 354595Sbill SEGITERATE(segno, cojumpfrom - 1,1,cointdest, 355595Sbill intdest, ubintdest, --){ 356630Shenry if (intdest->s_value <= dest->s_value) 357595Sbill break; /* beyond destination */ 358630Shenry if (intdest->s_tag <= JXQUESTIONABLE) 359595Sbill continue; /*frozen solid*/ 360630Shenry if (intdest->s_tag == JXALIGN){ 361630Shenry jumpfrom->s_jxoveralign = 1; 362595Sbill badjxalign++; 363595Sbill } 364630Shenry displ -= intdest->s_jxfear; 365595Sbill } 366595Sbill if (displ >= MINBYTE) { 367630Shenry jumpfrom->s_tag = JXINACTIVE; 368595Sbill } else { 369595Sbill stillactives++; 370595Sbill } 371595Sbill } /*end of backwards search*/ 372595Sbill } /*end of iterating through all symbols in this seg*/ 373595Sbill 374595Sbill if (nchange == 0) { 375595Sbill /* 376595Sbill * Now, if there are still active jxxx entries, 377595Sbill * we are partially deadlocked. We can leave 378595Sbill * these jxxx entries in their assumed short jump 379595Sbill * form, as all initial displacement calcualtions 380595Sbill * are hanging on unresolved jxxx instructions 381595Sbill * that might explode into a long form, causing 382595Sbill * other jxxxes jumping across the first set of 383595Sbill * jxxxes to explode, etc. 384595Sbill * However, if a jxxx jumps across a .align, 385595Sbill * we assume the worst for the deadlock cycle, 386595Sbill * and resolve all of them towards the long 387595Sbill * jump. 388595Sbill * Currently, the C compiler does not produce 389595Sbill * jumps across aligns, as aligns are only used 390595Sbill * in data segments, or in text segments to align 391595Sbill * functions. 392595Sbill */ 393595Sbill if (stillactives){ 394595Sbill SEGITERATE(segno, 0, 0, cojumpfrom, jumpfrom, 395595Sbill ubjumpfrom, ++){ 396630Shenry if (jumpfrom->s_tag == JXACTIVE){ 397630Shenry jumpfrom->s_tag = 398595Sbill badjxalign?JXNOTYET:JXINACTIVE; 399595Sbill } 400595Sbill } 401595Sbill if (badjxalign){ 402595Sbill jxxxbump(segno, (struct symtab **)0); 403595Sbill } 404595Sbill } 405595Sbill /* 406595Sbill * Handle all of the .align s 407595Sbill */ 408595Sbill SEGITERATE(segno, 0, 0, cojumpfrom, jumpfrom, 409595Sbill ubjumpfrom, ++){ 410630Shenry if (jumpfrom->s_tag == JXALIGN){ 411595Sbill /* 412595Sbill * Predict the true displacement 413595Sbill * needed, irregardless of the 414595Sbill * fact that we guessed 1 415595Sbill */ 416630Shenry displ = (jumpfrom->s_value - 1) & (unsigned)jumpfrom->s_jxfear; 417595Sbill if (displ == 0){ /*no virtual displacement*/ 418630Shenry jumpfrom->s_jxfear = -1; 419595Sbill } else { 420630Shenry jumpfrom->s_jxfear = (jumpfrom->s_jxfear + 1) - displ; 421595Sbill /* 422630Shenry * assert jumpfrom->s_jxfear > 0 423595Sbill */ 424630Shenry if (jumpfrom->s_jxfear == 1){ 425595Sbill /*our prediction was correct*/ 426595Sbill continue; 427595Sbill } 428595Sbill /* 429630Shenry * assert jumpfrom->s_jxfear > 1 430595Sbill */ 431630Shenry jumpfrom->s_jxfear -= 1; /*correct guess*/ 432595Sbill } 433595Sbill /* 434630Shenry * assert jumpfrom->s_jxfear = -1, +1...2**n-1 435595Sbill */ 436630Shenry jumpfrom->s_tag = JXNOTYET; /*signal*/ 437595Sbill jxxxbump(segno, cojumpfrom); 438630Shenry jumpfrom->s_tag = JXINACTIVE; 439595Sbill /* 440595Sbill * Assert jxfrom->jxvalue indexes the first 441595Sbill * code byte after the added bytes, and 442595Sbill * has n low order zeroes. 443595Sbill */ 444595Sbill } 445595Sbill } /*end of walking through each segment*/ 446595Sbill } /*end of no changes */ 447595Sbill else { /*changes, and still have to try another pass*/ 448595Sbill jxxxbump(segno, (struct symtab **)0); 449595Sbill } 450595Sbill } /*end of doing the topologic sort*/ 451595Sbill } /*end of iterating through all segments*/ 452595Sbill } /*end of jxxxfix*/ 453595Sbill 454595Sbill /* 455595Sbill * Go through the symbols in a given segment number, 456595Sbill * and see which entries are jxxx entries that have 457595Sbill * been logically "exploded" (expanded), but for which 458595Sbill * the value of textually following symbols has not been 459595Sbill * increased 460595Sbill */ 461595Sbill 462595Sbill jxxxbump(segno, starthint) 463595Sbill int segno; 464595Sbill struct symtab **starthint; 465595Sbill { 466595Sbill register struct symtab **cosp, *sp; 467595Sbill register struct symtab *ub; 468595Sbill register int cum_bump; 469595Sbill register unsigned char tag; 470595Sbill 471595Sbill cum_bump = 0; 472595Sbill SEGITERATE(segno, starthint, 0, cosp, sp, ub, ++){ 473630Shenry tag = sp->s_tag; 474595Sbill if (tag == JXNOTYET){ 475595Sbill #ifdef DEBUG 476595Sbill if (debug){ 477630Shenry if (sp->s_dest != 0) 478595Sbill printf("Explode jump to %s on line %d\n", 479630Shenry sp->s_dest->s_name, lineno); 480595Sbill else 481595Sbill printf("Explode an align!\n"); 482595Sbill } 483595Sbill #endif 484630Shenry sp->s_tag = JXINACTIVE; 485630Shenry sp->s_jxbump = 1; 486630Shenry cum_bump += sp->s_jxfear; 487595Sbill } 488595Sbill /* 489595Sbill * Only bump labels and jxxxes. Ignored entries can 490595Sbill * be incremented, as they are thrown away later on. 491595Sbill * Stabds are given their final value in the second 492595Sbill * pass. 493595Sbill */ 494595Sbill if (tag >= OKTOBUMP) /*only bump labels and jxxxes and floating stabs*/ 495630Shenry sp->s_value += cum_bump; 496595Sbill } 497630Shenry usedot[segno].e_xvalue += cum_bump; 498595Sbill } 499