!0.......^.........^.........^.. !B \H11\H01\H10\H07 GRAPHIC \H11\H07\H10\H00 DEFINITIONS !2.......^.........^.........^.........^.........^.........^.... Defining graphics on paper can be an arduous task especially when the finished item on-screen looks nothing like you imagined it. Toni Baker's got a much better idea ... and she's backed it up with machine code to boot! !1.......^.........^.........^.........^........ User-defined graphics are those things which appear on your screen when you press a letter of the alphabet while the cursor says 'G'. By suitable POKEing you can make any graphics look more or less like anything you want; for instance, graphic A could be a Peace symbol, graphic B could be a flower, and so on. But after a while you find that all this messing around with BIN and scraps of squared papers gets bit tedious, and you start to think how nice it would be to have a simple, foolproof way of defining any graphics character. That means having a powerful editing tool with a moveable cursor that will allow you to set and reset any pixel and see the results immediately, both magnified (so you can see the eight- by- eight layout explicitly), and 'life-size' (so you can see it as it really is). So, rather than pay ludicrous sums of money for someone else's graphics designing program, I decided to write my own. (As usual, I'm going to give it away to all you lucky people ... there's a lesson to be learnt there somewhere, but I'd rather not look for it!) !0.......^.........^.........^.. !B QUICK ON THE DRAW !1.......^.........^.........^.........^........ This is what happens when you run my program. In the top left-hand corner of the screen you get a larger than life picture of what the said graphic will look like. This image is eight squares across and eight squares tall with, of course, one square for each pixel of the finished graphic. Each square is either PAPER coloured (with an INK coloured border) or INK coloured (with a PAPER coloured border). In addition, you get a little dot in the middle of one of the squares, representing a cursor. You can move this cursor all over the place with the aid of the cursor controls, so if you make a mistake it's dead easy to go back and change it. In the middle of the screen you get a message saying 'GRAPHIC TO DEFINE A ="A"'. The letter won't always be 'A', of course - it depends on which graphic you're defining. The symbol in the quotes is the finished graphic, and is updated continuously as the graphic is changed. Below that you get a complete set of all the user-defined graphics, to let you see exactly what's what. !0.......^.........^.........^.. !B BEFORE ... !SYR06_59s1 !2.......^.........^.........^.........^.........^.........^.... Once you've got the code up and running, you'll be presented with a screen like that shown above. To begin defining your own graphics, simply press the key of the letter you wish to define; in the first illustration above, the 'C' key is undergoing transformation. Using the cursor keys, you can move the flashing dot (see the top left-hand pixel of the character square) and set and reset the individual cells to form the shape you require. Once you've designed your graphics alphabet, this will be displayed at the bottom of the screen so that you can identify them easily. !0.......^.........^.........^.. ... AND AFTER !SYR06_59s2 !1.......^.........^.........^.........^........ !B Once the program is running there are a number of things you can do. 1. Press a letter key (without Shift); for example, if you want to define graphic J then press the 'J' key, if you want to define graphic Q then press the 'Q' key, and so on. 2. Press a cursor key. This will move the cursor left, right, up or down as you'd expect. If you press Caps Shift at the same time then the pixel at the cursor position will be 'set', if you press Symbol Shift at the same time then the said pixel will be 'reset'. Without Shift, of course, everything stays unchanged. 3. Press any other number key, Space, or Enter (without Shift). This will have no effect whatsoever and will be a complete waste of time. 4. Press Symbol Shift and the '1' key. This will completely blank the character, resetting every pixel. 5. Press Caps Shift and the '1' key. This will completely fill in the character, setting every pixel. 6. Stop for a bit and put the kettle on. 7. Press either Shift key and the '4' key. This will invert the character, so that every pixel which was set becomes reset, and vice versa. 8. Press either Shift key and the '0' key. This will break out of the program and return you to normal Basic. 9. Press Symbol Shift and any number key other than a cursor key (or Space or Enter). This will reset one pixel without moving the cursor. 10.If the kettle has boiled, make the tea. 11.Press Caps Shift and any number key other than a cursor key (or Space or Enter). This will set one pixel without moving the cursor. 12.Press either of the Shift keys together with any letter key. This will re-define the said graphic as an exact copy of the graphics character for the letter key you've just pressed (which is slightly useful). 13.Drink the tea. (I don't mind if I do. Ed.) As you can see, it does quite a good job, and tries its humble best to make life really easy for the budding graphic designer. I've found that one of the most useful ways of using the machine code is to SAVE it under some name ("GrMC", say), and then to have a Basic program like this: !0.......^.........^.........^.. 10 LOAD "GrMC" CODE 20 RANDOMIZE USR 32854 30 INPUT "Name of program"; A$ 40 LOAD A$ !1.......^.........^.........^.........^........ You can always SAVE the Basic bit with a sensible name like 'Graphics', of course. This way, if you use SAVE "Graphics" LINE 10, then whenever you LOAD it you'll have your graphics designing set-up on the screen waiting for you. What's more, when you break out (using Shift and the '0' key) you'll be asked to name the Basic program with which you want to use the graphics you've just invented. (If you want to invent a new program at this point, then just break out using EDIT/ STOP/ Enter.) All this will of course be just as true for people with Microdrives, except that instead of LOAD you have to put LOAD *"m"; 1; (awkward isn't it?) and likewise for SAVE. The program is designed to sit at address 8000 Hex, which means that anyone who's only got the 16K Speccy will have to mess around trying to stick it somewhere else. The best way to do this is to change every address beginning '80' to a similar address with a different low byte, and every address beginning'81' to something else. For SAVEing purposes, you may like to know that the program takes up 361 (decimal) bytes, and that 8000 (Hex) is 32768 (decimal). !0.......^.........^.........^.. !B WHO'S A CHEAT? !1.......^.........^.........^.........^........ An interesting bit of program to watch out for is the stuff just above the label 'CHEAT'; that's where the program works out which machine code instruction it wants to execute and then POKEs it into the program before executing it. Thus, the program is self-modifying. There are two schools of thought regarding this sort of practice - the majority, who think it's cheating and thus shouldn't be allowed, and a minority of programmers who think it's actually quite clever. As you will have gathered I (without shame) subscribe to the second viewpoint; anyone now reaching for a pen to write that "encouraging bad programming practice" letter should note that I've seen it all before. Well, that's it, barring the code itself. Remember, to run it you must call the code from address 8056 Hex. !2.......^.........^.........^.........^.........^.........^.... !B DEFB 16 0C 00 47 52 41 50 48 49 43 20 54 4F 20 44 45 DEFB 46 49 4E 45 20 41 3D 22 90 22 0D 0D 41 42 43 44 DEFB 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 DEFB 55 0D 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D DEFB 9E 9F A0 A1 A2 A3 A4 This block of Hex is similar to Basic's Data statements; it holds information necessary for displaying information on the screen. The code should be organised at 8000h. !B Machine code Assembler Comments ---------.---------.------------------.------------------------- 3A1580 FIND_UDG LD A,(UDGLETTER) A:=the character code of an upper case letter name of a graphics character. ---------.---------.------------------.------------------------- 3D FIND_UDG2 DEC A 87 ADD A,A 87 ADD A,A 87 ADD A,A Multiply by eight 5F LD E,A 1600 LD D,#00 2A7B5C LD HL,(UDG) Point HL to graphic A 19 ADD HL,DE Point HL to required graphic C9 RET Above: This is a subroutine used by the main machine code program - when you get there you should be at 8047h. Below: This is the start of the machine code proper. To run the program, you need to call address 8056h. !B Machine code Assembler Comments ---------.---------.------------------.------------------------- 210000 START LD HL,#0000 FD7402 LD (TVFLAG),H Print to the lower part of the screen 22B05C LD (XY_COORDS),HLReset the graphics cursor ---------.---------.------------------.------------------------- CD4780 RESTART CALL FIND_UDG HL:=address of graphics character E5 PUSH HL Stack:=address of graphics character 210040 LD HL,D_FILE HL:=points to AT 0,0 position 0E08 LD C,#08 ---------.---------.------------------.------------------------- E3 LOOP_I EX (SP),HL 7E LD A,(HL) A:=the next pixel row 23 INC HL Point to next pixel row E3 EX (SP),HL 0608 LD B,#08 ---------.---------.------------------.------------------------- 17 LOOP_J RLA F5 PUSH AF Stack pixel information 9F SBC A,A A:=00 if pixel reset, FF is set 2F CPL A:=FF if pixel reset, 00 if set 77 LD (HL),A Print onto the screen 24 INC H Point to next row on screen EE7E XOR #7E A:=81 if pixel set, 7E if reset 1606 LD D,#06 ---------.---------.------------------.------------------------- 77 LOOP_K LD (HL),A Print onto the screen 24 INC H Point to next row on screen 15 DEC D 20FB JR NZ,LOOP_K Fill in body of character EE7E XOR #7E A:=FF if pixel set, 00 is reset 77 LD (HL),A Print onto the screen F1 POP AF A:=pixel information 1101F9 LD DE,#F901 19 ADD HL,DE Point to next char square 10E7 DJNZ LOOP_J Repeat for eight pixels 111800 LD DE,#0018 19 ADD HL,DE Point to next line of squares 0D DEC C 20DA JR NZ,LOOP_I Repeat for each row of pixels E1 POP HL Balance the stack 3AB05C LD A,(Y_COORD) A:=Y coord of the graphics cursor 0F RRCA 0F RRCA 0F RRCA FDB677 OR (X_COORD) A:=8*Y+X coordinates 6F LD L,A 2643 LD H,#43 Point HL to appropriate point on the screen 7E LD A,(HL) A:=18 or 7E accordingly EE18 XOR #18 A:=99 or 66 to mark cursor 77 LD (HL),A Mark cursor on screen 24 INC H Point to next row on screen 77 LD (HL),A Mark cursor on screen 014700 LD BC,#0047 BC:=length of message 110080 LD DE,#8000 DE:=start of message CD3C20 CALL #203C Print message on screen ---------.---------.------------------.------------------------- FDCB016E WAIT BIT 5,(FLAGS) 28FA JR Z,WAIT Wait until key is pressed FDCB01AE RES 5,(FLAGS) Signal ready for next key CD8E02 CALL KEY_SCAN A:=character code of digit or capital letter on key pressed (or 0D, 0E or 20 for Enter, both Shift keys or Space, respectively) 3A045C LD A,(KSTAT_4) D:=the Shift key status FE40 CP #40 14 INC D 381D JR C,NOTLETTER Jump unless letter pressed 2810 JR Z,CHANGEUDG Jump if no Shift pressed CD4A80 CALL FIND_UDG2 HL:=address of UDG for key pressed E5 PUSH HL CD4780 CALL FIND_UDG HL:=the address of the UDG specified D1 POP DE DE:=the address of the UDG for the key pressed EB EX DE,HL 010800 LD BC,#0008 EDB0 LDIR Copy the UDG 1808 JR EXIT_1 Jump to exit ---------.---------.------------------.------------------------- 321580 CHANGEUDG LD (UDGLETTER),A Change current UDG name C64F ADD A,#4F A:=the UDG character 321880 LD (UDG_CHAR),A Insert into the message ... ---------.---------.------------------.------------------------- C35680 EXIT_1 JP START And start all over again ---------.---------.------------------.------------------------- 284C NOTLETTER JR Z,NO_SHIFT Jump only if no Shift keys were pressed FE30 CP "0" C8 RET Z Exit the program if Shift and "0" were pressed FE31 CP "1" 2016 JR NZ,TEST_4 Jump unless Shift and "1" were pressed 7A LD A,D FE19 CP #19 2804 JR Z,RESET_UDG Jump if Symbol Shift and "1" were pressed ---------.---------.------------------.------------------------- 0EFF SET_UDG LD C,#FF Set all the pixels of C 1802 JR BLANK_UDG ---------.---------.------------------.------------------------- 0E00 RESET_UDG LD C,#00 Reset all the pixels of C ---------.---------.------------------.------------------------- CD4780 BLANK_UDG CALL FIND_UDG HL:=address of UDG specified 0608 LD B,#08 ---------.---------.------------------.------------------------- 71 BL_LOOP LD (HL),C 23 INC HL 10FC DJNZ BL_LOOP Blank the entire UDG 186B JR EXIT_2 Jump to exit ---------.---------.------------------.------------------------- FE34 TEST_4 CP "4" 200D JR NZ,NEW_PIX Jump unless Shift and "4" pressed CD4780 CALL FIND_UDG HL:=address of UDG specified 0608 LD B,#08 ---------.---------.------------------.------------------------- 7E INV_LOOP LD A,(HL) A:=next row of pixels 2F CPL Change to inverse video 77 LD (HL),A Invert the row 23 INC HL Point to the next row 10FA DJNZ INV_LOOP Invert the whole character 185A JR EXIT_2 Jump to exit ---------.---------.------------------.------------------------- F5 NEW_PIX PUSH AF Push the character code 7A LD A,D FE20 CP #20 9F SBC A,A A:=00 if Caps Shift pressed, FF otherwise E608 AND #08 A:=00 or 08 EEDF XOR #DF A:=DF or D7 FDAE77 XOR (X_COORD) 07 RLCA 07 RLCA 07 RLCA 322881 LD (CHEAT),A Specify the appropriate set or reset instruction CD4780 CALL FIND_UDG HL:=address of UDG specified FD4E76 LD C,(Y_COORD) 0600 LD B,#00 09 ADD HL,BC Point HL to row containing pixel at cursor position CB00 DEFB CB 00 ---------.---------.------------------.------------------------- CHEAT $-1 F1 POP AF A:=code of key pressed ---------.---------.------------------.------------------------- FE35 NO_SHIFT CP "5" 2818 JR Z,LEFT Jump if "cursor left" FE36 CP "6" 282A JR Z,DOWN Jump if "cursor down" FE37 CP "7" 281B JR Z,UP Jump if "cursor up" FE38 CP "8" 202C JR NZ,EXIT_2 Exit unless "cursor right" ---------.---------.------------------.------------------------- 3AB15C RIGHT LD A,(X_COORD) FE07 CP #07 2825 JR Z,EXIT_2 FD3477 INC (X_COORD) Move cursor right 1820 JR EXIT_2 ---------.---------.------------------.------------------------- 3AB15C LEFT LD A,(X_COORD) A7 AND A 281A JR Z,EXIT_2 FD3577 DEC (X_COORD) Move cursor left 1815 JR EXIT_2 ---------.---------.------------------.------------------------- 3AB05C UP LD A,(Y_COORD) A7 AND A 280F JR Z,EXIT_2 FD3576 DEC (Y_COORD) Move cursor up 180A JR EXIT_2 ---------.---------.------------------.------------------------- 3AB05C DOWN LD A,(Y_COORD) FE07 CP #07 2803 JR Z,EXIT_2 FD3476 INC (Y_COORD) Move cursor down ---------.---------.------------------.------------------------- C35F80 EXIT_2 JP #805F !1.......^.........^.........^.........^........ !B -- from Your Spectrum 6 (Aug.1984) -- !$