(Comp.sys.handhelds) Item: 3834 by catto at author.ecn.purdue.edu Author: [Erin S Catto] Subj: Fuzz & ML Blurbs Date: Tue Aug 13 1991 Well I promised myself that I would pass on what I've learned once I figured out what's going on with this machine language business. Sorry, it's not a game. Anyway, here's a small program which simply draws random dots on the stack display and exits when you press the drop key. I thank Jan Brittenson for the STAR assembler (excellent) and the MLDL (way excellent). This program covers most of the key hardware questions a freshie might have. + Drawing on the stack display at a coordinate (0-127,0-63) + Data storage acquisition so that data tables are temporary + Turning off keyboard interupts (go ahead, press ON-A-F, nothing happens) + Machine code keyboard scanning + How to get random numbers you can't depend on I'll briefly cover a few topics: Preserving the RPL Operating System =================================== The HP48 has an operating system just like any other computer. It was written in System RPL and machine code. The HP48 OS uses the registers d0, d1, b, and d to take care of business, such as memory management and RPL command pointing. Thus you must save these important register and restore them at the termination of your machine code program. You must also set the OS back on track. Here's how it's done: save_regs = #0679B ;ml routines to handle these tasks rest_regs = #067D2 begin call.a save_regs ;save registers . . . call.a rest_regs ;restore registers move.a @d0, a ;a = next RPL instruction add.a 5, d0 ;point d0 to following instruction jump @a ;jump to instruction in a ;address of object prolog? end Here's how I believe the last three instructions work: d0 | v RPL instruction stream: ... 11EC7|65547|EF9D7|83188|... move.a @d0, a ;a gets 74556 add.a 5, d0 ;does the following d0 | v RPL instruction stream: ... 11EC7|65547|EF9D7|83188|... Lets say at the address 74556 there is the RPL program prolog 02D9D then jump @a will jump to the address 02D9D, a chain of pointers. Now the operating system is back on track, refreshing the screen, handling the keyboard, etc. Evidence of this is seen when you exit the attached program, the RPL OS automatically returns the screen to its normal state. Drawing on the Stack Display ============================ The stack display contains two grobs: the stack-message grob and the menu grob. Statistics: + Screen Grob: location of grob contained at #7055B size = 56 rows high by 136 bits wide (34 nibbles) + Menu Grob: location of grob contained at #70551 size = 8 rows high by 136 bits wide Once you get to the grob in memory you must skip the 20 nibble prolog to get to the pixel data. The pixel data begins at the upper left corner of the display and goes left to right, wrapping to the next row after 34 nibbles. While the nibbles go left to right as the address increases the bits in each nibble are in right to left order. Example: On Screen: 0101 In memory: 1010 = #A It is useful to have a data table which contains the address of the start of each row of pixels. This way you can take an arbitrary y coordinate and transform it into an index to the data simply by multiplying y by 5. Lets look at this: Say y = 4, then 5 * y = 20, now the data table looks like this: start y=0 y=1 y=2 y=3 y=4 | | | | | v v v v v 04347|26347|48347|6A347|8C347..... (stream of row addresses) So you use start+20 to get the row address. Below I talk about building a data string. Here's my plug for using the stack display instead of PICT. 1) Let PICT be the user's territory. 2) PICT sits and takes up 1098 bytes, unless you PURGE it. 3) The stack display is always the same size (131x64) Making Data Space ================= Quite often data tables are used in mcode programs. If the data is going to be generated by the program then it becomes a waste of space to attach non- executing data to your program. A routine at #05B7D will make a string object whose data length (not including the header) is specified by the contents of the c reqister. The address is returned in the address register d0. d0 v C2A20|04000|123DF8734872394729834... ...2312| <- header->|<--- length = c -----... ...--->| All you need to do is: move.p5 c, length ;you specify length call make_str ;make_str = #05B7D swap.a a, d0 ;d0 = string data area address move.w a, r0 ;store the address somewhere for reference Turning Off Interupts ===================== Turning off interupts means turning off the keyboard, in a manner of speaking. It means your program will only be interupted when you want input. It is simple to turn the interupts off, but you must be sure to turn them back on. Interupts Off: clrb 15, st ;clear bit 15 of status register intoff Interupts On: inton setb 15, st ;set bit 15 of status register It would be nice if someone in-the-know would explain the flags the ST register contains. Keyboard input, the ML way =========================== The instructions which must be used to grab key presses are OUT and IN. Sounds simple enough, sure if you know how to do it. Here's how it's done 1) load register c with the row of keys you want to check (see table below) 2) perform OUT.X C to tell your HP to check that row 3) call IN_C = #01160, this address only contains: IN.4 C RET It seems that IN.4 C must be called, I think it messes with the return stack. 4) move the adress field of register c somewhere safe, say register a 5) clear register c 6) perform OUT.X C to tell your HP to stop checking 7) now if you stored register c in register a in step (4) then the low nibbles of reg. a will contain bits set corresponding to the keys pressed in the row of interest. The Keyboard Map: (from an article by Jan Brittenson) (IN) #20 #10 #08 #04 #02 #01 --------------------------- (OUT) | 6 5 4 3 2 1 #100 | B C D E F #080 | PRG CST VAR ^ NXT #040 | STO EVL < v > #020 | COS TAN sqt pwr inv #010 | ON* ENT +/- EEX DEL <== #008 | alp SIN 7 8 9 / #004 | yel MTH 4 5 6 x #002 | blu A 1 2 3 - #001 | ' 0 . SPC + (*) The ON key is actually in a column of its own. #xxx refers to the keyboard scan bits. The column scan bit for the ON key is #8000. The numbers in the leftmost column are use to specify the row in the OUT command and the numbers in the topmost row correspond to a positive result from the IN command. Note that these numbers have a binary representation which has only one bit set. So multiple bits set by the IN command mean multiple key presses. The Random Thing ================ Your HP48sx maintains a hardware checksum at #00104 which is constantly changing in a seemingly unpredictable manner. You can get four random nibbles at a time from the above address which may be masked down to the range of numbers you are interested in. The program below takes two random numbers from the hardware crc, one for the x coordinate and one for the y coordinate. Two dummy instructions were planted inbetween the two readings. It turns out that without these two dummy instructions, the static on the screen reduces to two predictable lines. Notice ====== I claim no proficiency at ML programming, but maybe this info will help others get started. I did not hack out much of this. Most of it came from studying the source code for SCHIP v1.0 by Erik Bryntse. For those not familiar with this type of programming I suggest you actually run the program below and recognize that only one pixel is being draw at a time...it's FAST!!! By the way, press the drop key to EXIT. The ML Freshie, Zoom catto@ecn.purdue.edu P.S. Has anyone finished Ant? You must see the final round, it's a hoot. The rolling (:() is actually quite easy, once you know how to get by it. There's a lot more game after that. --------------------------begin STAR source------------------------------------ ;FUZZ: makes random static on screen, drop key exits ;r0 = data string pointer ;r1 = x coordinate in pixels ;r2 = key data storage make_str = #05B7D save_regs = #0679B rest_regs = #067D2 crc_val = #00104 screen_ptr = #7055B menu_ptr = #70551 in_c = #01160 header code call save_regs ;save d0, d1, b, d clr.w c ;c = 0 move.w c, r2 ;initialize key storage clr.w a ;a = 0 move.p3 #140, c ;c = data string size (64*5+4) call make_str ;make a data string swap.a a, d0 ;a = string address move.w a, r0 ;save string address in r0 move.a a, d0 ;point to string move.5 screen_ptr, d1 ;d1 = address of screen grob move.p2 56, a ;a = # rows in screen grob call fill_table ;make data table move.5 menu_ptr, d1 ;d1 = address of menu grob move.p2 8, a ;a = # rows in menu grob call fill_table ;complete data table clrb 15, st ;interupt flag off intoff ;turn off interupts main: move.5 crc_val, d0 ;d0 = address of hardware crc move.a @d0, c ;c = pseudo random # clr.a a ;a = 0 move.p2 #7F, a ;a = mask for screen width and.a a, c ;c = random and x mask move.w c, r1 ;save x coordinate move.5 in_c, d0 ;these two lines do nothing move.a @d0, c ;except make the crc random ;it's a numbers game move.5 crc_val, d0 ;point to crc again move.a @d0, c ;c = pseudo random # move.p2 #3F, a ;a = mask for screen height and.a a, c ;c = random and y mask move.a c, a ;a = c = y coordinate add.a c, c ;c = 2*c add.a c, c ;c = 4*c add.a a, c ;c = 5*c move.w r0, a ;get data string address add.a a, c ;add offset for y coordinate move.a c, d0 ;point to row address move.a @d0, c ;get row address move.w r1, a ;get x coordinate in pixels srb.w a ;a = x/2 (shift right bit) srb.w a ;a = x/4 = x coordinate in nibs add.a c, a ;a = row start + (x offset) move.a a, d0 ;d0 points to nib of interest move.w r1, c ;c = x coordinate in pixels clr.a a ;a = 0 move.p1 3, a ;a = 3 (#11b) and.a c, a ;get pixel position in nib clr.a c ;c = 0 move.p1 1, c ;set up c ;mirror_nib routine makes a nibble containing one bit set a x coordinate. ;While grob nibbles are in order from left to right (up in memory), ;the bits within the nibble are in reverse order. So this routine ;puts the nibble through a u-turn. mirror_nib: dec.a a ;a = a - 1 ;if a(old) = 0 then sets carry brcs ready ;jump if carry is set add.a c, c ;c = 2*c = shift left bit c jump mirror_nib ;loop ;Now the old nibble of the lcd grob is xor-ed with the new nibble ;BTW a nibble is just a four bit chunk, a hex digit (ex. #1111b = #Fh = #15d) ready: clr.a a ;xor old and new nibble move.1 @d0, a ;a = old nibble (OLD) clr.w d move.a c, d ;d = new nibble (NEW) move.a a, c ;a = c = OLD and.a d, c ;c = OLD and NEW swap.a c, d ;d = OLD and NEW or.a a, c ;c = OLD or NEW not.a d ;d = not(OLD and NEW) and.a d, c ;c = OLD xor NEW move.1 c, @d0 ;place xor nibble move.p3 #010, c ;#010 specifies the row of out.x c ;the drop key, the out.x ;instruction scans a row call in_c ;in_c returns the results ;you have to call this command ;something to do with return ;stack move.a c, a ;a = results clr.a c ;c = 0 out.x c ;stop scanning ;the following checks if the drop was pressed AND released for program exit ;that way your not pushing the drop key when program exits move.w r2, c ;get old key status move.w a, r2 ;put new key status not.a a ;a = keys not pressed and.a c, a ;and keys that were pressed brbs 0, a, exit ;bit 0 of contains flag jump main ;for drop key column ;fill_table: make a data table containing addresses of lcd rows ;IN: d1 points to grob data ; d0 points to data string ; a = # rows to set up ;AFFECTS: a, c, d0 fill_table: move.a @d1, c ;c = grob adress add.a 16, c ;add 20 to skip grob header add.a 4, c ftlp: move.a c, @d0 ;put row address into table add.a 16, c ;add 34 to skip to next row add.a 16, c ;lcd grob width = 34 * 4 = 136 add.a 2, c add.a 5, d0 ;skip to next address space dec.b a ;decrement row counter brnz.b a, ftlp ;continue if counter <> 0 ret exit: inton ;interupts on (keyboard) setb 15, st ;flag interupts on call rest_regs ;recall d, b, d1, d0 move.a @d0, a ;return to RPL operating system add.a 5, d0 jump @a endcode -------------------------------end STAR source---------------------------------