Tetris is the hottest new puzzle game right now. Needless to say, everyone wants a piece of it (pun very much intended.) The game's title comes from the word "Tetromino," a geometric shape composed of four squares connected orthogonally. The game features seven unique pieces in the shape of O, I, S, Z, L, J and T. Each piece can fit a 4x4 grid:

The goal is to place tetrominoes in a well, organizing them into complete rows, which then disappear. If the stack reaches the top of the field, it's game over.
Since all pieces fit a 4x4 grid, and each cell is either solid or empty, we only need one 16-bit INTEGER per pattern. In binary form, 16-bit INTEGERs look as follows (the most significant bit is located to the left and the least significant bit to the right:)
bit 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 dec
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 = 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 = 1 (2^0)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 = 2 (2^1)
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 = 3 (2^0 + 2^1)
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 = 4 (2^2)
0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 = 137 (2^0 + 2^3 + 2^7)
etc.
Instead of seeing 16-bit integers as one string of sixteen cells, we may display them as grids of 4 lines and 4 columns, starting with the four most significant bits (top row) and ending with the four least significant bits (bottom row.) In that representation, the most significant bit (2^15, or 32,768 in decimal) will be in the top left cell, and the least significant bit (2^0, or 1 in decimal) in the bottom right cell (this cell is also the origin point for the block.) When a cell is solid, the matching bit is set. If the cell is empty, the bit is clear:

Note that in the hexadecimal representation, each character (right to left) represents a line (bottom to top.) It's not very important, but it can help visualize what's going on internally.
Some pieces have four different orientations (L, J and T,) others only two (I, S and Z,) and O doesn't really rotate at all. To keep things simple, we're going to assume they all have four rotations (although some will be repeats.) Each piece is thus defined using only four 16-bit integers (8 bytes of memory:)

The following code snippet shows how to store and decode blocks, as well as perform rotations and find the pattern currently in use:
' Basic operations
DIM pattern(0 TO 27) AS INTEGER ' Patterns (rotated blocks)
DIM plBlk AS INTEGER ' Player block index, 0 to 6
DIM plRot AS INTEGER ' Player block orientation, 0 to 3
DIM plPat AS INTEGER ' Actual pattern code
' Initialize randomizer
RANDOMIZE TIMER
' Decode DATA statements
FOR i% = 0 TO 27
READ s$
pattern(i%) = VAL("&h" + s$)
NEXT i%
' Select block randomly
plBlk = INT(RND * 7)
' Rotate
plRot = (plRot + 1) AND &h3 ' Clockwise
plRot = (plRot + 3) AND &h3 ' Counter-clockwise
' Pattern code
plPat = pattern((plBlk * 4) + plRot)
' 7 blocks x 4 orientations = 28 patterns (hexadecimal)
DATA 0660,0660,0660,0660
DATA 2222,0F00,2222,0F00
DATA 0360,4620,0360,4620
DATA 0630,2640,0630,2640
DATA 4460,0740,0622,02E0
DATA 2260,0470,0644,0E20
DATA 0270,0464,0E40,2620
Memory-efficiency aside, using 16-bit integers also makes simple math and boolean operators very convenient manipulation tools. For instance we can isolate the top row of a pattern by creating a mask. We first set a variable to 61,440 (or &hF000 in hexadecimal,) and use it in conjunction with the AND boolean operator and a pattern:

It is also possible to shift patterns to the left or right by simply dividing or multiplying by a power of two. For instance, performing an integer division by 2 will shift bits to the right once, dividing by 4 will shift bits to the right twice:
24 = 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0
24 \ 2 = 12 = 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
24 \ 4 = 6 = 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0
etc.
And of course, we can also shift bits to the left:
8 = 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
8 * 2 = 16 = 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
8 * 4 = 32 = 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
etc.
In theory, using divisions and multiplications to perform bit shifting works, but only on UNSIGNED integers, which QuickBASIC doesn't support. In fact, this method will break on negative integers (that is, when the most significant bit is set) or when the absolute value reaches 32,768.
The simplest way to solve the issue would be to use 32-bit integers. Alternatively, we may rebuild SHL ("<<") and SHR (">>",) two bitwise operators found in C. Our function would discard the most significant bit before shifting and restore it at the end of the operation:
DEFINT A-Z
DECLARE FUNCTION SHR% (i AS INTEGER, b AS INTEGER)
DECLARE FUNCTION SHL% (i AS INTEGER, b AS INTEGER)
DECLARE FUNCTION BIN$ (i AS INTEGER)
CLS
v% = &HE187
PRINT "INIT. "; BIN$(v%); " "; v%
' Shift bits left and right
LOCATE 3
FOR i% = 0 TO 16
PRINT "SHL"; i%; TAB(8); BIN$(SHL%(v%, i%)); " "; SHL%(v%, i%); TAB(40);
PRINT "SHR"; i%; TAB(47); BIN$(SHR%(v%, i%)); " "; SHR%(v%, i%)
NEXT i%
''
'' Displays integer (16 bits) in binary form, most significant bit first
''
FUNCTION BIN$ (i AS INTEGER)
t$ = STRING$(16, "0")
FOR b% = 0 TO 15
IF (i AND (2 ^ b%)) THEN MID$(t$, 16 - b%, 1) = "1"
NEXT b%
BIN$ = t$
END FUNCTION
''
'' Shift Bits Left
''
FUNCTION SHL% (i AS INTEGER, b AS INTEGER)
DIM s AS INTEGER
SELECT CASE b
CASE IS < 1
SHL% = i
CASE IS > 15
SHL% = 0
CASE ELSE
s = (2 ^ (15 - b))
IF (i AND s) THEN
SHL% = (i AND (s - 1)) * (2 ^ b) OR &H8000
ELSE
SHL% = (i AND (s - 1)) * (2 ^ b)
END IF
END SELECT
END FUNCTION
''
'' Shift Bits Right
''
FUNCTION SHR% (i AS INTEGER, b AS INTEGER)
SELECT CASE b
CASE IS < 1
SHR% = i
CASE IS > 15
SHR% = 0
CASE ELSE
IF (i AND &H8000) THEN
SHR% = ((i XOR &H8000) \ (2 ^ b)) OR (2 ^ (15 - b))
ELSE
SHR% = i \ (2 ^ b)
END IF
END SELECT
END FUNCTION
Like patterns, the board is stored in an array of 16-bit integers. Each element of the array contains a 16-column row, which is more than enough. The length of the array defines the height of the board.
DIM SHARED grid(0 TO 22) AS INTEGER ' Game board, 23 rows DIM plX AS INTEGER, plY AS INTEGER ' Player block coordinates
Remember that the least significant bit (smallest value) is written to the right, and most significant bit (biggest value) to the left. However, this goes against the default graphic coordinates where the leftmost column is index 1 and the rightmost column is index 80. Therefore, plX (the horizontal position of the block) should be reversed when testing for collisions. The illustrations below assume that X is 3 units away from the RIGHT border, Y is 6 units from the top. To keep things sane, entries in the grid() array follow the screen coordinates logic: 0 is top and 22 is bottom. The origin point of the block in the bottom right corner.

Now that we know about masking and bitshifting, we have everything we need to test for collisions. Basically, we'll do exactly what we've being doing above to obtain a 4x4 window of the game board, then we mask the block value with the window value to determine potential collisions:

If the result is 0 then no collision happened. If the result is anything else, then at least one solid cell from the window overlaps a solid cell from the block, therefore the move is invalid. Collision tests should be made ahead to confirm (or cancel) horizontal and vertical moves.
' Test collision
IF (moveValid%(plX, plY, plPat)) THEN
PRINT "Can move to "; plX; " "; plY
ELSE
PRINT "Cannot move to "; plX; " "; plY
END IF
''
'' Returns true if pattern P located at X, Y is not colliding
'' with the game board.
''
FUNCTION moveValid% (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DIM w AS INTEGER, x2 AS INTEGER
' Invert horizontal axis
x2 = 15 - x
' Get 4x4 window
w = ((SHL%((SHR%(grid(y - 3), x2) AND &HF), 12)) OR _
(SHL%((SHR%(grid(y - 2), x2) AND &HF), 8)) OR _
(SHL%((SHR%(grid(y - 1), x2) AND &HF), 4)) OR _
((SHR%(grid(y), x2) AND &HF)))
' Return collision status
moveValid% = ((w AND p) = 0)
END FUNCTION
To prevent breaking boundaries, padding cells should be added to the board. Three columns on both sides (that leaves ten columns for gameplay) and three bottom lines should be enough. These padding cells shouldn't be displayed on screen.
' Place 6 padding columns (3 on either side) and 3 padding rows. FOR i = 0 TO 19 grid(i) = &HE007 NEXT i grid(20) = &HFFFF grid(21) = &HFFFF grid(22) = &HFFFF
If a block collides while going down, it has landed and should be merged with the board. The OR boolean operator is perfect for this task as it preserves all bits that are set:

Rather than shift the game board left and right, we're going to focus on the pattern value this time and do the complete opposite. First, we isolate each row. Then, we shift the bits of each value left to align with plX. Finally we merge each shifted row with the corresponding board line.

The illustration above shows how to break the pattern value into four distinct rows, the code below goes further by aligning each row to the plX position (remember plX must be reversed so it becomes relative to the right edge rather than the left edge,) and the board merge with the OR boolean operator:
'' '' Merge pattern P in game board at X, Y. '' SUB blockMerge (x AS INTEGER, y AS INTEGER, p AS INTEGER) DIM x2 AS INTEGER ' Invert horizontal axis x2 = 15 - x ' Place piece grid(y - 3) = grid(y - 3) OR SHL%((SHR%(p, 12) AND &HF), x2) grid(y - 2) = grid(y - 2) OR SHL%((SHR%(p, 8) AND &HF), x2) grid(y - 1) = grid(y - 1) OR SHL%((SHR%(p, 4) AND &HF), x2) grid(y) = grid(y) OR SHL%(p AND &HF, x2) END SUB
When the block is placed, we look for (and crush) completed rows (those whose value is &hFFFF,) starting down and going up. The three padding rows (22, 21, and 20) are ignored for the whole process.
''
'' Clear rows
''
SUB clearRow
DIM count AS INTEGER, i AS INTEGER, j AS INTEGER
' Count completed lines (ignore bottom padding)
FOR i = 19 TO 0 STEP -1
count = count - (grid(i) = &HFFFF)
NEXT i
' No row completed
IF (count = 0) THEN EXIT SUB
' Search for complete rows
FOR i = 19 TO 1 STEP -1
' This row is complete
IF (grid(i) = &HFFFF) THEN
' Search for next potential incomplete row
FOR j = i - 1 TO 0 STEP -1
' This row is incomplete
IF (grid(j) <> &HFFFF) THEN
' Make complete row swallow incomplete row
grid(i) = grid(j)
grid(j) = &HFFFF
EXIT FOR
END IF
NEXT j
END IF
NEXT i
' Clear top rows (restore left and right padding columns)
FOR i = 0 TO count - 1
grid(i) = &HE007
NEXT i
END SUB
And that's it. Now we give the player another block, reset plX and plY coordinates, and give back control to the player. Test for collision as soon as a new block is given to make sure the player can actually do something. If the block is colliding right away, the game is over.
There's no need for amazing graphics, so we'll just stick to the good old 80-columns-by-25-lines text mode. To maintain square aspect ratio, cells will span two columns; As said previously, most of the rendering involves reading values backward to match the text mode coordinates system. The following routines use X for the horizontal position and Y for the vertical position:
''
'' Draw grid at X, Y. Rendering is done top to bottom, right (smaller value)
'' to left (bigger value.) The three bottom rows, and columns 0, 1, 2, 13, 14,
'' 15 are not drawn (replaced by # symbol here.)
''
SUB drawGrid (x AS INTEGER, y AS INTEGER)
DIM i AS INTEGER, j AS INTEGER, s AS STRING * 32
' Display offset
LOCATE y
' Go through each row (top to bottom, ignore bottom padding)
FOR i = 0 TO 19
' Test all 16 cells (columns, right to left)
s = SPACE$(32)
FOR j = 15 TO 0 STEP -1
IF (grid(i) AND (2 ^ j)) THEN
MID$(s, 1 + (15 - j) * 2, 2) = CHR$(219) + CHR$(219)
END IF
NEXT j
' Draw
LOCATE , x: PRINT "######"; MID$(s, 7, 20); "######"; i
NEXT i
PRINT STRING$(32, "#"); 20
PRINT STRING$(32, "#"); 21
PRINT STRING$(32, "#"); 22
END SUB
''
'' Draw pattern P at X, Y. Patterns are rendered bottom to top,
'' and right to left. Each cell takes two columns. Empty cells
'' do not erase screen content.
''
SUB drawPattern (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DIM tmp AS INTEGER, i AS INTEGER
' Copy pattern value
tmp = p
' Process all four rows, starting from the bottom
FOR i = 3 TO 0 STEP -1
' Test all four cells
LOCATE y + i
IF (tmp AND &H1) THEN LOCATE , x + 6: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H2) THEN LOCATE , x + 4: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H4) THEN LOCATE , x + 2: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H8) THEN LOCATE , x: PRINT CHR$(219); CHR$(219);
' Shift bits right, crop lowest row
tmp = SHR%(tmp, 4)
NEXT i
END SUB
The following listing covers the basics and leaves a lot of room for improvement (player score, show next piece, sound, controlled randomness, improved graphics, better controls...) Be creative and have fun!
DECLARE SUB drawGrid (x AS INTEGER, y AS INTEGER)
DECLARE SUB drawPattern (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DECLARE SUB blockMerge (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DECLARE SUB clearRow ()
DECLARE FUNCTION moveValid% (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DECLARE FUNCTION SHR% (i AS INTEGER, b AS INTEGER)
DECLARE FUNCTION SHL% (i AS INTEGER, b AS INTEGER)
CONST flgUpdateGrid = &H1 ' Request: refresh game board
CONST flgMoveDown = &H2 ' Request: move pice down
DIM SHARED grid(0 TO 22) AS INTEGER ' Game board, 23 rows
DIM pattern(0 TO 27) AS INTEGER ' Patterns (rotated blocks)
DIM plBlk AS INTEGER ' Player block index, 0 to 6
DIM plRot AS INTEGER ' Player block orientation, 0 to 3
DIM plPat AS INTEGER ' Actual pattern code
DIM plX AS INTEGER, plY AS INTEGER ' Player block coordinates
DIM plKey AS STRING, plInp AS INTEGER ' Player keyboard input string, scancode
DIM flags AS INTEGER ' Generic program flags
DIM nextPush AS SINGLE ' Next push timer
'' Setup patterns ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Decode DATA statements
FOR i% = 0 TO 27
READ s$
pattern(i%) = VAL("&h" + s$)
NEXT i%
'' Setup grid ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Place 6 padding columns (3 on either side) and 3 padding rows.
FOR i = 0 TO 19
grid(i) = &HE007
NEXT i
grid(20) = &HFFFF
grid(21) = &HFFFF
grid(22) = &HFFFF
'' Initial block, clear screen, request grid draw ''''''''''''''''''''''''''''
' Initialize randomizer
RANDOMIZE TIMER
' Give block
plBlk = INT(RND * 7)
plPat = pattern(plBlk * 4)
plX = 9: plY = 3
' Clear screen, request grid draw
CLS
flags = flgUpdateGrid
nextPush = timer + 1.0
'' Main loop '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
DO
'' TIMED PUSH DOWN REQUEST ''
IF (nextPush < timer) then
nextPush = timer + 1.0
flags = flags OR flgMoveDown
END IF
'' PLAYER INPUT ''
' Get scancode
plKey = INKEY$
IF (LEN(plKey) = 1) THEN
plInp = ASC(plKey)
ELSEIF (LEN(plKey) = 2) THEN
plInp = CVI(plKey)
ELSE
plInp = 0
END IF
' Process scancode
SELECT CASE plInp
CASE 27 ' escape (exit)
EXIT DO
CASE &H4B00 ' left arrow (move left)
IF (moveValid%(plX - 1, plY, plPat)) THEN
plX = plX - 1
flags = flags OR flgUpdateGrid
END IF
CASE &H4D00 ' right arrow (move right)
IF (moveValid%(plX + 1, plY, plPat)) THEN
plX = plX + 1
flags = flags OR flgUpdateGrid
END IF
CASE &H4800 ' up arrow (rotate)
tmpRot% = (plRot + 1) AND &H3
tmpPat% = pattern((plBlk * 4) + tmpRot%)
IF (moveValid%(plX, plY, tmpPat%)) THEN
plRot = tmpRot%
plPat = tmpPat%
flags = flags OR flgUpdateGrid
END IF
CASE &H5000 ' down arrow (move down)
flags = flags OR flgMoveDown
nextPush = timer + 1.0
END SELECT
'' PUSH PIECE DOWN ''
IF (flags AND flgMoveDown) THEN
' Move is valid
IF (moveValid%(plX, plY + 1, plPat)) THEN
plY = plY + 1
' Cannot move down, merge
ELSE
blockMerge plX, plY, plPat
clearRow
' Give another block
plBlk = INT(RND * 7) ' Pick block at random
plPat = pattern(plBlk * 4) ' Catch pattern code
' Reset position and rotation
plX = 9: plY = 3: plRot = 0
' Reset input queue
DEF SEG = &h40
POKE &h1A, PEEK(&h1C)
DEF SEG
' If the block is stuck, game over
IF (moveValid%(plX, plY, plPat) = 0) THEN
EXIT DO
END IF
END IF
flags = (flags OR flgUpdateGrid) XOR flgMoveDown
END IF
'' RENDER ''
' Redraw game board
IF (flags AND flgUpdateGrid) THEN
' Draw grid
drawGrid 1, 1
' Draw block
COLOR 15
drawPattern 1 + ((plX - 3) * 2), 1 + (plY - 3), plPat
COLOR 7
' Toggle flag off
flags = flags XOR flgUpdateGrid
END IF
LOOP
' 7 blocks x 4 orientations = 28 patterns (hexadecimal)
DATA 0660,0660,0660,0660
DATA 2222,0F00,2222,0F00
DATA 0360,4620,0360,4620
DATA 0630,2640,0630,2640
DATA 4460,0740,0622,02E0
DATA 2260,0470,0644,0E20
DATA 0270,0464,0E40,2620
''
'' Merge pattern P in game board at X, Y.
''
SUB blockMerge (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DIM x2 AS INTEGER
' Invert horizontal axis
x2 = 15 - x
' Place piece
grid(y - 3) = grid(y - 3) OR SHL%((SHR%(p, 12) AND &HF), x2)
grid(y - 2) = grid(y - 2) OR SHL%((SHR%(p, 8) AND &HF), x2)
grid(y - 1) = grid(y - 1) OR SHL%((SHR%(p, 4) AND &HF), x2)
grid(y) = grid(y) OR SHL%(p AND &HF, x2)
END SUB
''
'' Clear rows
''
SUB clearRow
DIM count AS INTEGER, i AS INTEGER, j AS INTEGER
' Count completed lines (ignore bottom padding)
FOR i = 19 TO 0 STEP -1
count = count - (grid(i) = &HFFFF)
NEXT i
' No row completed
IF (count = 0) THEN EXIT SUB
' Search for complete rows
FOR i = 19 TO 1 STEP -1
' This row is complete
IF (grid(i) = &HFFFF) THEN
' Search for next potential incomplete row
FOR j = i - 1 TO 0 STEP -1
' This row is incomplete
IF (grid(j) <> &HFFFF) THEN
' Make complete row swallow incomplete row
grid(i) = grid(j)
grid(j) = &HFFFF
EXIT FOR
END IF
NEXT j
END IF
NEXT i
' Clear top rows (restore left and right padding columns)
FOR i = 0 TO count - 1
grid(i) = &HE007
NEXT i
END SUB
''
'' Draw grid at X, Y. Rendering is done top to bottom, right (smaller value)
'' to left (bigger value.) Ideally the three bottom rows, and columns 0, 1, 2,
'' 13, 14, 15 shouldn't be drawn.
''
SUB drawGrid (x AS INTEGER, y AS INTEGER)
DIM i AS INTEGER, j AS INTEGER, s AS STRING * 32
' Display offset
LOCATE y
' Go through each row (top to bottom, ignore bottom padding)
FOR i = 0 TO 19
' Test all 16 cells (columns, right to left)
s = SPACE$(32)
FOR j = 15 TO 0 STEP -1
IF (grid(i) AND (2 ^ j)) THEN
MID$(s, 1 + (15 - j) * 2, 2) = CHR$(219) + CHR$(219)
END IF
NEXT j
' Draw
LOCATE , x: PRINT "######"; MID$(s, 7, 20); "######"; i
NEXT i
PRINT STRING$(32, "#"); 20
PRINT STRING$(32, "#"); 21
PRINT STRING$(32, "#"); 22
END SUB
''
'' Draw pattern P at X, Y. Patterns are rendered bottom to top,
'' and right to left. Each cell takes two columns. Empty cells
'' do not erase screen content.
''
SUB drawPattern (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DIM tmp AS INTEGER, i AS INTEGER
' Copy pattern value
tmp = p
' Process all four rows, starting from the bottom
FOR i = 3 TO 0 STEP -1
' Test all four cells
LOCATE y + i
IF (tmp AND &H1) THEN LOCATE , x + 6: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H2) THEN LOCATE , x + 4: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H4) THEN LOCATE , x + 2: PRINT CHR$(219); CHR$(219);
IF (tmp AND &H8) THEN LOCATE , x: PRINT CHR$(219); CHR$(219);
' Shift bits right, crop lowest row
tmp = SHR%(tmp, 4)
NEXT i
END SUB
''
'' Returns true if pattern P located at X, Y is not colliding
'' with the game board.
''
FUNCTION moveValid% (x AS INTEGER, y AS INTEGER, p AS INTEGER)
DIM w AS INTEGER, x2 AS INTEGER
' Invert horizontal axis
x2 = 15 - x
' Get 4x4 window
w = ((SHL%((SHR%(grid(y - 3), x2) AND &HF), 12)) OR _
(SHL%((SHR%(grid(y - 2), x2) AND &HF), 8)) OR _
(SHL%((SHR%(grid(y - 1), x2) AND &HF), 4)) OR _
((SHR%(grid(y), x2) AND &HF)))
' Return collision status
moveValid% = ((w AND p) = 0)
END FUNCTION
''
'' Shift Bits Left
''
FUNCTION SHL% (i AS INTEGER, b AS INTEGER)
DIM s AS INTEGER
SELECT CASE b
CASE IS < 1
SHL% = i
CASE IS > 15
SHL% = 0
CASE ELSE
s = (2 ^ (15 - b))
IF (i AND s) THEN
SHL% = (i AND (s - 1)) * (2 ^ b) OR &H8000
ELSE
SHL% = (i AND (s - 1)) * (2 ^ b)
END IF
END SELECT
END FUNCTION
''
'' Shift Bits Right
''
FUNCTION SHR% (i AS INTEGER, b AS INTEGER)
SELECT CASE b
CASE IS < 1
SHR% = i
CASE IS > 15
SHR% = 0
CASE ELSE
IF (i AND &H8000) THEN
SHR% = ((i XOR &H8000) \ (2 ^ b)) OR (2 ^ (15 - b))
ELSE
SHR% = i \ (2 ^ b)
END IF
END SELECT
END FUNCTION
- Mike Hawk