JCCyC Posted September 23, 2014 Share Posted September 23, 2014 (edited) Probably lots of people had this idea already, and I've been reading about TMS9900 assembly only sporadically. Let CALLEE be the address of a subroutine like this: CALLEE (instruction) (instruction) (instruction) ... RTWP And my idea for a "reentrant call": STWP R8 S 36,R8 MOV R8, @32(R8) MOV CALLEE, @34(R8) BLWP @32(R8) Could be turned into a macro, and the trashed register cound be any one that doesn't have a special use (R12-R15). I did this on paper only, so it may have a few holes. Also, this is NOT for hardware interrupts! And, of course, for machines with a respectable amount of RAM -- definitely not a base 99/4A! So, makes sense? Is it horribly slow and wasteful and a much better method exists? EDIT: No, wait, wait. I don't need that additional space for the transfer vector. It can be located in the middle of the new workspace. STWP R8 S 32,R8 MOV R8, @8(R8) MOV CALLEE, @10(R8) BLWP @8(R8) EDIT II, THE SEQUEL: And if I didn't mind trashing the first few registers of my current WP I could even make them overlap! What do you think? Edited September 23, 2014 by JCCyC Quote Link to comment Share on other sites More sharing options...
marc.hull Posted September 23, 2014 Share Posted September 23, 2014 You could stack all your regs by simply adding 32 to the ws vector before issuing a blwp and then subtracting 32 after the return. At least that's how it looks from here. Maybe that's what your thinking? Quote Link to comment Share on other sites More sharing options...
JCCyC Posted September 23, 2014 Author Share Posted September 23, 2014 You could stack all your regs by simply adding 32 to the ws vector before issuing a blwp and then subtracting 32 after the return. At least that's how it looks from here. Maybe that's what your thinking? Yep. Alas, there seems to be no easy way to load the WP with a calculated value other than BLWP, and for that I have to make up a memory structure with the new WP and the new PC. And now that you mention it, increase-on-push and decrease-on-push stacks are equally doable -- but with the decrease-on-push stack you can do the overlap trick without trashing critical registers. Quote Link to comment Share on other sites More sharing options...
Stuart Posted September 23, 2014 Share Posted September 23, 2014 Alas, there seems to be no easy way to load the WP with a calculated value other than BLWP, and for that I have to make up a memory structure with the new WP and the new PC. If you store the calculated value in the word after a LWPI instruction, and then execute that instruction, I think that would do it? 1 Quote Link to comment Share on other sites More sharing options...
JCCyC Posted September 23, 2014 Author Share Posted September 23, 2014 If you store the calculated value in the word after a LWPI instruction, and then execute that instruction, I think that would do it? Ooooo, self-modifying code. Naughty! I like it! 1 Quote Link to comment Share on other sites More sharing options...
Gary from OPA Posted September 23, 2014 Share Posted September 23, 2014 yep, self-modifying code can be very useful, as long as you are not using a cpu running 99105 or 68689 with pre-fetch pipleline then it could fail. Quote Link to comment Share on other sites More sharing options...
JCCyC Posted September 23, 2014 Author Share Posted September 23, 2014 yep, self-modifying code can be very useful, as long as you are not using a cpu running 99105 or 68689 with pre-fetch pipleline then it could fail. I'll say! I got epically burned by that when developing a DOS device driver in 1990! Everything worked OK in PCXTs and crashed badly in PCATs. And when we debugged step-by-step... ta-da, worked again! 16-byte prefetch queue. (8088 has only four.) To be fair, the LWPI idea could avoid that by constructing it somewhere other than the next instructions and reaching it with a branch/jump. 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 24, 2014 Share Posted September 24, 2014 I always use a method I learned from mathew180's assembly tutorials. R10 is designated to be stack pointer. I always use BL instead of BLWP for my own routines. LI R10,STACK * Initialize stack pointer (once) ... BL @MYSUB * Call subroutine ... MYSUB MOV R11,*R10+ * Push return address onto the stack ... * Return DECT R10 * Pop return address off the stack MOV *R10,R11 B *R11 Quote Link to comment Share on other sites More sharing options...
Willsy Posted September 24, 2014 Share Posted September 24, 2014 TI describe the idea of chaining/stacking workspaces in one of their 9900 data books. Can't remember which one. The books hail back to the mid 70's. Quote Link to comment Share on other sites More sharing options...
gregallenwarner Posted September 24, 2014 Share Posted September 24, 2014 I always use a method I learned from mathew180's assembly tutorials. R10 is designated to be stack pointer. I always use BL instead of BLWP for my own routines.I've always used R13 as the stack pointer, since I never use BLWP for my own routines, that means R13-R15 are freed up for other uses. Saves R10 for another general purpose register. Quote Link to comment Share on other sites More sharing options...
RXB Posted September 24, 2014 Share Posted September 24, 2014 RXB uses a replacement for CALL LINK that is 5 times faster and very small foot print. Mostly as it does no checking or pushing and popping onto the VDP Stack for saving stuff. Instead the Registers are preloaded before the CALL EXECUTE ******************************************************* * CPU PROGRAM FOR >8300 SCATCH PAD SUBROUTINE EXECUTE * ******************************************************* * AORG >8300 CPUPGM DATA >8302 * CPUPGM DATA >8302 First address. * DATA >0420 * BLWP >834A Switch contex * DATA >834A * FAC not used * DATA >04E0 * CLR @>837C Clear for GPL * DATA >837C * * DATA >045B * RT Return to GPL. * * END * ******************************************************* EXECUT CALL SPNUM1 EXAGN CALL SUBLP3 MOVE 12,@VAR0,V@VROAZ Save CPU values MOVE 12,G@CPUPGM,@VAR0 Load PGM DST @FAC,@VARY Load address XML >F0 Execute address MOVE 12,V@VROAZ,@VAR0 Restore CPU values CEQ >B3,@CHAT Comma? BS EXAGN Repeat BR LNKRTN It does save the 12 bytes at >8300 before the CALL EXECUTE(cpu-address) in VDP and FAC has the address to execute assembly. So it restores the 12 bytes at >8300 and returns to XB, unless there is another address like you could do this: CALL EXECUTE(cpu-address,cpu-address,cpu-address) The cool thing in all the Registers are preloaded and this is extremely faster then CALL LINK in normal XB. Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted September 24, 2014 Share Posted September 24, 2014 I'll say! I got epically burned by that when developing a DOS device driver in 1990! Everything worked OK in PCXTs and crashed badly in PCATs. And when we debugged step-by-step... ta-da, worked again! 16-byte prefetch queue. (8088 has only four.) To be fair, the LWPI idea could avoid that by constructing it somewhere other than the next instructions and reaching it with a branch/jump. I construct most of my non-recursive routines in this manner. Matthew explains it in a later post in the same thread. http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-3?do=findComment&comment=2021522 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted September 27, 2014 Share Posted September 27, 2014 RXB uses a replacement for CALL LINK that is 5 times faster and very small foot print. Mostly as it does no checking or pushing and popping onto the VDP Stack for saving stuff. Instead the Registers are preloaded before the CALL EXECUTE I've been doing some speed tests on CALL EXECUTE compared to CALL LINK to find out how much time it takes to start up an assembly program. The CALL EXECUTE assembly program is simply RTWP to return to XB. For CALL LINK the program is: LWPI WKSP LWPI >83E0 B @>006A So these don't really do anything other than to test how long it takes to get into an assembly subroutine and then return to XB. After determining how long a loop takes (10 N=N+1::CALL LINK("FILL")::GOTO 10) and then subtracting the time taken by the N=N+1 and GOTO I found: CALL EXECUTE takes 14.04ms to get into the A/L sub and return to XB CALL LINK takes 15.44 ms if the subprogram is 1st in the list CALL LINK takes 37.54 ms if the subprogram is 257th on the list. (Most packages such as TML or XB256 have 30 to 40 subroutines - if the routine is the 40th in the DEF table then it would take about 18.88 ms) If you do the math you find that it takes about .086 ms to read each entry in the DEF table If you are not passing variables then CALL EXECUTE is somewhat faster. One thing to take away from this is that when you are setting up your DEF table, the last entry in the A/L code is the first to be checked by CALL LINK, so the subroutines where speed is really important should have their entries among the last in the DEF table. The speed situation changes when you are passing variables to the A/L subprogram. Unless I'm missing something, the only way to pass values to CALL EXECUTE is using CALL LOAD to preload the registers with the desired values. (Rich, correct me if I am wrong on this) So for example, if my workspace is at 10000 and I want 123 in R3 then I have to: 10 CALL LOAD(10006,0,123)::CALL EXECUTE(address) That CALL LOAD negates any speed advantage for CALL EXECUTE. According to my tests: a CALL LINK that fetches 1 value to a register from XB using NUMREF and CFI takes 24.04 ms a CALL EXECUTE that preloads 1 register with CALL LOAD takes 32.84ms a CALL LINK that fetches 8 values to registers from XB using NUMREF and CFI takes 83.74 ms a CALL EXECUTE that preloads 8 registers with CALL LOAD takes 95.64 ms 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted September 27, 2014 Share Posted September 27, 2014 (edited) I've been doing some speed tests on CALL EXECUTE compared to CALL LINK to find out how much time it takes to start up an assembly program. The CALL EXECUTE assembly program is simply RTWP to return to XB. For CALL LINK the program is: LWPI WKSP LWPI >83E0 B @>006A So these don't really do anything other than to test how long it takes to get into an assembly subroutine and then return to XB. After determining how long a loop takes (10 N=N+1::CALL LINK("FILL")::GOTO 10) and then subtracting the time taken by the N=N+1 and GOTO I found: CALL EXECUTE takes 14.04ms to get into the A/L sub and return to XB CALL LINK takes 15.44 ms if the subprogram is 1st in the list CALL LINK takes 37.54 ms if the subprogram is 257th on the list. (Most packages such as TML or XB256 have 30 to 40 subroutines - if the routine is the 40th in the DEF table then it would take about 18.88 ms) If you do the math you find that it takes about .086 ms to read each entry in the DEF table If you are not passing variables then CALL EXECUTE is somewhat faster. One thing to take away from this is that when you are setting up your DEF table, the last entry in the A/L code is the first to be checked by CALL LINK, so the subroutines where speed is really important should have their entries among the last in the DEF table. The speed situation changes when you are passing variables to the A/L subprogram. Unless I'm missing something, the only way to pass values to CALL EXECUTE is using CALL LOAD to preload the registers with the desired values. (Rich, correct me if I am wrong on this) So for example, if my workspace is at 10000 and I want 123 in R3 then I have to: 10 CALL LOAD(10006,0,123)::CALL EXECUTE(address) That CALL LOAD negates any speed advantage for CALL EXECUTE. According to my tests: a CALL LINK that fetches 1 value to a register from XB using NUMREF and CFI takes 24.04 ms a CALL EXECUTE that preloads 1 register with CALL LOAD takes 32.84ms a CALL LINK that fetches 8 values to registers from XB using NUMREF and CFI takes 83.74 ms a CALL EXECUTE that preloads 8 registers with CALL LOAD takes 95.64 ms The idea behind CALL EXECUTE is you only have to pass values once. To make a point this is the CALL LINK in XB: *********************************************************** * LINK INSTRUCTION : SE Sep 1980 *********************************************************** * FORMAT: * CALL LINK("file-name",parameter1,parameter2,...) * * LINK ROUTINE READS THE FILE NAME SPECIFIED BY THE USER A * SAVE THE ADDRESS OF THE NAME FOR LATER USE. THE FILE WIL * BE SEARCHED IN UTILITY CODE LATER ON. * * PARAMETERS ARE PASSED EITHER BY REFERENCE OR BY VALUE. * NUMERIC OR STRING VARIABLES AND NUMERIC OR STRING ARRAYS * ARE PASSED BY REFERENCE AND ALL OTHERS INCLUDING A USER * DEFINED FUNCTION ARE PASSED BY VALUE. * * PARAMETER INFORMATION IS STORED IN CPU >8300 THROUGH >83 * THAT GIVES A PARAMETER TYPE CODE OF EACH PARAMETER. * CODE 0 ... Numeric expression * CODE 1 ... String experession * CODE 2 ... Numeric variable * CODE 3 ... String variable * CODE 4 ... Numeric array * CODE 5 ... String array * * IF A PARAMETER IS PASSED AS A NUMERIC EXPRESSION ITSL * ACTUAL VALUE GETS PUSHED INTO THE VALUE STACK. IN CASE O * A STRING EXPRESSION , ITS VALUE STACK CONTAINS AN ID(>65 * POINTER TO THE VALUE SPACE AND ITS LENGTH. IF A PARAMETE * GETS PASSED AS A REFERENCE THE PRODUCT OF XML SYM AND XM * SMB IN THE @FAC AREA GETS PUSHED INTO STACK. * * AFTER AN ASSEMBLY LANGUAGE SUBPROGRAM IS EXECUTED LINK * ROUTINE WILL POP THE STACK TO GET RID OF PARAMETER * INFORMATION. CONTROL WILL BE TRANSFERED TO THE XB MAIN * PROGRAM AFTERWARDS. * *********************************************************** * CALL LINK program *********************************************************** LINKIT CALL CHKIN Check if INIT has been called DST @VSPTR,@OLDS Save VSPTR for later use CEQ LPARZ,@CHAT Check for "(" BR ERRSYN XML PGMCHR Advance program pointer XML PARSE Get the routine name. BYTE RPARZ * Read up to ")" CEQ >65,@FAC2 Should be a string BR ERRBA DCZ @FAC6 Don't accept null string BS ERRBA CH 6,@FAC7 Should be less then 6 char BS ERRBA XML VPUSH Push to make it semi-permanen CLR @COUNT Initialize parameter counter *********************************************************** * PARAMETERS get evaluated here *********************************************************** PAR01 CEQ RPARZ,@CHAT No arg. So execute it BS EXE01 CEQ COMMAZ,@CHAT Should have a comma BR ERRSYN DST @PGMPTR,@ERRCOD Save text pointer XML PGMCHR Get the character CHE >80,@CHAT Must be an expression BS VAL01 * If CHAT = LPARZ then pass by expression CALL CLRFAC Clear FAC entry for SYM XML SYM Read in the symbol table info * After XML SYM @FAC area contains a pointer to symbo table * Below statement checks if it is a UDF. CLOG >40,V*FAC Pass by value BR VAL01 CEQ COMMAZ,@CHAT Pass by reference BS REF01 CEQ RPARZ,@CHAT Pass by reference BS REF01 CEQ LPARZ,@CHAT An array BS ARRAY CHE >80,@CHAT Pass by value BS VAL01 BR ERRSYN *********************************************************** * ARRAY case gets checked here *********************************************************** * Should look like A(,,) etc. * Stack entry for an array will look like * +--------------+-------+---+-------------+--------------- * | Pointer to | >00 | | Pointer to | * | symbol table | or | | dim info in | * | entry | >65 | | real v.s. | * +- FAC --------+ FAC2 -+---+- FAC4 ------+- FAC6 -------- * ARRAY XML PGMCHR Get the next character CEQ RPARZ,@CHAT Pass by reference BS ARRAY2 CEQ COMMAZ,@CHAT More array information BS ARRAY DDEC @PGMPTR Adjust the pointer ST LPARZ,@CHAT BR REF01 Pass by reference * In array cases the symbol table address gets stored at FA * area, and the pointer to the value space (dimension info) * goes into FAC4 ARRAY2 XML PGMCHR Advance the program pointer CLOG >80,V*FAC Test string bit BR GC39D ST 4,*COUNT Numeric array BR GC3A1 GC39D ST 5,*COUNT String array case * Check if array is being shared. If it is then go back * through the linkage to get the actuals symbol table * pointer. Put the pointer to the value space (dimension in * into FAC4. GC3A1 CLOG >20,V*FAC Shared array? BS GC3BE MOVE 2,V@6(@FAC),@FAC4 If so, get pointer CLOG >20,V@-6(@FAC4) Shared also? BS GC3BC MOVE 2,V*FAC4,@FAC4 Array is not shared GC3BC BR GC3C5 GC3BE DST @FAC,@FAC4 Array is not shared DADD 6,@FAC4 Point to value space GC3C5 BR PUSH *********************************************************** * VALUE * Passing the parameter by value *********************************************************** VAL01 DST @ERRCOD,@PGMPTR Restore program pointer XML PGMCHR Skip the first character DST @BYTES,@TEMP In case of passing a string XML PARSE Parsing up to comma BYTE RPARZ DST @TEMP,@BYTES Restore the value in >0C area * After parsing @FAC area contains its actual numeric value * in a numeric case, and the following information in a * string case. * +----------------+-----+--+------------+----------------- * | >001C or | >65 | | Pointer to | Length of string * | value pointer | | | string | string * | address | | | | * +- FAC ----------+-FAC2+--+-FAC4 ------+- FAC6 ---------- * CGT >63,@FAC2 If more then 99 then BR GC3E0 ST 1,*COUNT Store flag for string express BR GC3E3 GC3E0 CLR *COUNT Otherwise it is a numeric exp GC3E3 BR PUSH Push into stack *********************************************************** * REFERENCE * Passing the parameter by reference *********************************************************** * Variables, array element and whole array passing. * * After SMB @FAC entry shold look like; * +--------------+------+-----+-------------+-------------- * | Pointer to | >00 | | Pointer to | * | symbol table | | | value space | * | entry | | | | * +-- FAC -------+ FAC2 +-----+- FAC4 ------+- FAC6 ------- * for numeric case, and * +--------------+------+-----+-------------+-------------- * | Pointer to | >65 | | Pointer to | String * | value space | | | string | length * | entry | | | | * +- FAC --------+ FAC2 +-----+- FAC4 ------+- FAC6 ------- * for a string case. REF01 XML SMB Get the location CHE >B8,@CHAT Pass array expression BS VAL01 CZ @FAC2 BR GC3F6 ST 2,*COUNT Must be a numeric variable BR PUSH GC3F6 ST 3,*COUNT Must be a string variable *********************************************************** * PUSH routine * Pushes @FAC entry into a value stack. *********************************************************** PUSH INC @COUNT CGT 16,@COUNT Too many parameters BS ERRBA XML VPUSH BR PAR01 Get the next argument. *********************************************************** * EXECUTE routine * Restore file name info transfer control over to ALC *********************************************************** EXE01 ST >20,@FAC Store blank in the FAC area. MOVE 5,@FAC,@FAC1 MOVE 4,V@12(@OLDS),@STORE Get the file name info MOVE @STORE+2,V*STORE,@FAC Move to FAC DCLR @ERRCOD Clear program pointer for * error code XML ALSUP Go to CPU at >2000 to execute BS ERROR Error found * If no error, start checking s *********************************************************** * RETURN to the XB main program. *********************************************************** NOERR DCH @OLDS,@VSPTR Pop the stack BR GC429 XML VPOP Pop the stack B NOERR GC429 B LNKRTN Check ")" and end of statemen *********************************************************** * SUBROUTINES used in this file. *********************************************************** CLRFAC CLR @FAC MOVE 7,@FAC,@FAC1 RTN ********************************************************** As you can see it has tons of housekeeping and does lots of PUSH and POP onto the stack. (CALL EXECUTE has zero housekeeping.) The more CALL LINKs the less memory you have available for Assembly and the slowing it gets. (Your tests agree with me on this.) If you need to push values more often with CALL EXECUTE then pass the address of that variable in memory to Assembly, now with CALL LINK you can do this but you do cut the amount of RAM for Assembly and pay a price of slowing execution. (More LINKs and more variable passing.) I would think once you get used to using Assembly like my game IN THE DARK uses CALL EXECUTE instead of CALL LINK. (Most of the variables are all kept in Assembly lower 8K) I could have preloaded the locations of the variable into Registers and that would be a space saver. Thus when Assembly changes variables no pass of values is needed. (CALL LOAD/PEEK) But overall thanks for the tests, and keep up the good work. Edited September 27, 2014 by RXB Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted September 28, 2014 Share Posted September 28, 2014 (edited) It looks like you are using BLWP to choose from a bunch of workspaces, each of which contains unique numbers useful to the subroutine, and the numbers were created at the same time as the assembly program. So you're using canned numbers and not actually passing variables to the subroutine. For what it's worth, this can be done the same way in CALL LINK by having the 1st instruction be LWPI >xxxx. This is a bit bulkier ( by 8 bytes) and a bit slower than CALL EXECUTE. If you wanted to write an assembly equivalent for, say, CALL HCHAR then the XB program needs to calculate the values for row, column, ascii, and # repeats. These would need to be passed to the program and with CALL EXECUTE you'd need to use CALL LOAD to get the values into the registers before CALL EXECUTE. In this case, CALL LINK will be faster then CALL EXECUTE. All this got me thinking about an improvement to CALL LINK that runs at the same speed regardless of how many subroutines there are. You could create a jump table ending at >4000. That's essentially what the DEF table is, but you wouldn't use the subroutine name; just the addresses of the subroutines. At >2004 put a pointer to the start of the jump table. Then you would need to modify the CALL LINK subroutine lookup routine which starts at >205A. The code below is one way to do it. This expects 2 digits giving a number from "00" to "99" CLINK MOV @>834A,R0 put the two byte long name in R0 AI R0,>CFD0 subtract >30 from each byte, 1st byte has tens digit; 2nd byte has ones digit MOV R0,R2 SWPB R2 ones digit is now in MSB SRL R2,7 multiply ones digit by 2 SRL R0,8 tens digit is now in LSB LI R1,20 MPY R1,R0 multiply tens digit by 20 A R1,R2 now R2 has offset in jump table A @>2004,R2 add jump table offset MOV *R2,R2 get address and put into R2 B *R2 run subroutine AORG >2000 change pointer to routine DATA CLINK You would CALL LINK("23",optional varialbles) to go to the 23rd subprogram in the jump table. This expects 2 numeric characters in the program name for CALL LINK, so subprograms from 00 to 99 could be accessed. (If you used A to Z you could have 676 subprograms) With this method, there is no speed penalty if there are a lot of subroutines in the list, but you still have the option of passing variables from or to XB as usual in CALL LINK. This would be only 10% slower than CALL EXECUTE but with the ability to pass variables easily. Also, remember that the assembly subroutines take some time, so the actual amount of slowdown would be less. Edited September 28, 2014 by senior_falcon Quote Link to comment Share on other sites More sharing options...
RXB Posted September 28, 2014 Share Posted September 28, 2014 The entire main loop in my game IN THE DARK has this Main Loop: 160 ! MAIN LOOP 170 CALL SOUND(50,-6,15) 180 U=W+351+C :: CALL LOAD(U,225)! FOOTSTEPS 190 CALL LOCATE(#1,90,C*8-7) :: CALL MOVES("RV",768,W,0)! SHOW ME 200 ! ARROWS OR L,R,D,U,SPACE,ENTER,FCTN7,P,p 210 CALL ONKEY(Z$,0,K,Z)GOTO 370,370,370,410,410,410,450,450,450,510,510,510,580,690,810,1590,1590 220 ! ENEMY MOVEMENTS 230 IF F+T<9 THEN 210 240 CALL PEEK(-12276,G) :: IF G=218 THEN E=I*5 :: CALL LOAD(-12276,0) 250 IF M<>B THEN 280 260 IF E=0 THEN 270 ELSE E=E-1 :: GOTO 210 270 IF U-X<256 AND M=B THEN CALL SOUND(-1,2500,2) 280 CALL AMSBANK(M,M+1) :: CALL EXECUTE(-12256) 290 CALL PEEK(-12286,G,H) :: X=(G*256)+H :: IF U=X AND B=M THEN 750! MISSION FAILED 300 IF(U<X AND M>B)OR(X<U AND B>M)THEN M=B :: CALL LOAD(-12286,33,129) 310 IF X<8227 AND M=0 THEN 360 320 IF X>16348 AND M=42 THEN 360 330 IF X<8227 THEN M=M-1 :: X=X+4096 340 IF X>16348 THEN M=M+1 :: X=X-4096 ELSE 360 350 N=INT(X/256) :: O=X-(N*256) :: CALL AMSBANK(M,M+1) :: CALL LOAD(-12286,N,O) 360 CALL AMSBANK(B,B+1) :: GOTO 190 370 ! MOVE LEFT 380 CALL GCHAR(12,C-1,G) :: IF G=143 OR G=119 OR G=111 OR G=103 THEN 640 390 IF G=122 THEN 760 400 C=C-1 :: GOTO 160 410 ! MOVE RIGHT 420 CALL GCHAR(12,C+1,G) :: IF G=143 OR G=119 OR G=111 OR G=103 THEN 640 430 IF G=122 THEN 760 440 C=C+1 :: GOTO 160 450 ! MOVE DOWN 460 IF W=15488 AND B=42 THEN 1020 470 CALL GCHAR(13,C,G) :: IF G=143 OR G=119 OR G=111 OR G=103 THEN 640 480 IF G=122 THEN 760 490 IF W>=15615 THEN B=B+1 :: W=W-4096 :: GOTO 570 500 W=W+32 :: GOTO 160 510 ! MOVE UP 520 IF W=8192 AND B=0 THEN CALL HCHAR(1,1,119,480) :: CALL MOVES("VR",768,0,W) :: GOTO 640 530 CALL GCHAR(11,C,G) :: IF G=143 OR G=119 OR G=111 OR G=103 THEN 640 540 IF G=122 THEN 760 550 IF W=8192 THEN B=B-1 :: W=W+4096 :: GOTO 570 560 W=W-32 :: GOTO 160 570 CALL AMSBANK(B,B+1) :: GOTO 160 580 ! FEEL AROUND ME 590 CALL CHAR(128,"0000187E7E242466") 600 N=INT(U/256) :: O=U-(N*256) :: CALL LOAD(-12282,N,O) 610 CALL EXECUTE(-12034) 620 CALL PEEK(-12288,N,O) :: F=F+N :: S=S+(I*N) :: T=T+O 630 FOR Z=1 TO N+O :: CALL SOUND(1,-5,3) :: NEXT Z :: CALL CHAR(128,"187E5A5A3C242424") :: GOTO 190 640 CALL SOUND(99,200,20,-7,0) :: GOTO 160 650 ! SHOW SCORE 660 CALL MOVES("VR",768,0,W) :: CALL CLEAR :: CALL HPUT(10,1,"TRAPS=",10,7,T,10,13,"SCANS=",10,19,S,10,24,"FOOD=",10,29,F) 670 CALL HPUT(14,1,"YOU ARE INJURED "&STR$(D)&" TIMES!") :: GOTO 1570 680 ! SHOW AREA SCAN 690 IF S THEN 700 ELSE GOSUB 660 :: GOTO 160 700 CALL CLEAR :: CALL SOUND(10,2E4,6,770,5,440,6,-7,3) :: U=W+352 :: K=U+32 :: N=352 :: O=384 :: FOR Z=1 TO 12 710 CALL MOVES("RV",32,U,N,"RV",32,K,O) :: U=U-32 :: K=K+32 :: N=N-32 :: O=O+32 :: NEXT Z 720 CALL COLOR(11,16,1,12,7,1) :: FOR Z=1 TO 100 :: CALL KEY(0,K,J) :: IF K=13 THEN 740 730 NEXT Z :: GOTO 700 740 CALL COLOR(11,2,1,12,2,1) :: S=S-1 :: GOTO 160 750 ! GAME OVER FAILED 760 FOR Z=1 TO 24 STEP 4 :: CALL SOUND(100,-7,Z) :: NEXT Z 770 D=D+1 :: IF I=0 THEN I=1 ELSE I=I-1 :: E=I 780 GOSUB 660 :: IF U=X AND M=B THEN CALL HPUT(22,5,"REPAIR BOT CAUGHT YOU!",20,12,"YOU DIE!") :: END 790 IF D=25 THEN CALL HPUT(22,2,"YOUR INJURIES ARE TO GREAT...",20,12,"YOU DIE!") :: END 800 GOTO 120 If I had used CALL LINK instead of CALL EXECUTE then reading a value would still need a CALL PEEK value for a single value to check. I did use CALL LOAD for 2 values and CALL PEEK for those same values. But those only needed to be checked or loaded when needed. I could have had all of this handled in Assembly totally but I wanted to use my RXB CALL ONKEY new routine instead of an Assembly SCAN routine. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted September 29, 2014 Share Posted September 29, 2014 RXB wrote: If I had used CALL LINK instead of CALL EXECUTE then reading a value would still need a CALL PEEK value for a single value to check. ------------------------ Not so. CALL LINK can do much more than simply starting up an assembly language program. You have forgotten the optional parameter list that can be used. In your program, 280 - - - - -::CALL EXECUTE(-12256)290 CALL PEEK(-12286,G,H) :: X=(G*256)+H ::- - - - - could be: 290 CALL LINK("PRNAME",X) If written for it, the A/L sub PRNAME can take the value at -12286, put it at FAC, convert to floating point, then use NUMASG to pass the value back to XB as the value X. All this conversion sounds tedious, but it actually turns out to be somewhat faster than using EXECUTE and PEEK. This ability to easily pass strings and numbers back and forth between assembly and XB is one of the great things about CALL LINK. I don't think Commodore or Atari had anything like it. Pages 277-280 and 284-287 of the EA manual cover this. The equates for XB are on pages 415-416. You can use HMLOADER which is part of the Missing Link package to fool the XB loader into embedding assembly code in high memory. This is all handled automatically by HMLOADER. One possible killer for your purposes is that the word at >2000 has to point to the lookup routine in high memory. Except for that word, the entire low memory can be used if needed. Quote Link to comment Share on other sites More sharing options...
RXB Posted September 29, 2014 Share Posted September 29, 2014 (edited) Hmm that is why I do not use Floating Point as it is slower than integer math by huge factor. Every number using CALL LINK is Floating Point passed to or back to XB. Only way to get integers is to convert them first using GINT routine but that requires you load them using CALL INIT. Or use a CALL PEEK or CALL LOAD, then then again they are ALWAYS Floating Point so I fail to see the advantage you gain. I did not use any routines built into XB CALL INIT for that reason. By the way the Lower 8K in my game IN THE DARK uses 960K SAMS memory. All of the Assembly in game is in Higher Memory with no need for LINK names. I really should have done a better job of just passing the location of those Variables that way I could just use CALL PEEK and CALL LOAD once at set up and never again. Just have the Assembly modify those variables and XB would use those variables when it returns so no calculations needed as already done. And no variables would ever need to be passed back and forth from XB to Assembly as both would use the same ones. And I do know about the CALL INIT Assembly Routines as my first Project was WINDYXB a pure Assembly version that later became RXB in GPL using Assembly too. Edited September 29, 2014 by RXB Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted September 29, 2014 Share Posted September 29, 2014 (edited) Hmm that is why I do not use Floating Point as it is slower than integer math by huge factor. Every number using CALL LINK is Floating Point passed to or back to XB. Every number (except for line numbers) in XB is floating point. Just because there is no decimal point does not mean it is not a floating point number. From your program: 290 CALL PEEK(-12286,G,H) :: X=(G*256)+H :: IF U=X AND B=M THEN 750! MISSION FAILED Both G and H are integers on the assembly side but are converted to floating point by PEEK and then passed back as floating point numbers to XB. (edit) Did some speed tests: 10 A=3::B=7 20 N=N=1:: X=A*B::GOTO 20 -after subtracting the overhead of the loop X=A*B takes 6.45 ms to multiply the "integers" After changing line 10 to A=.0003::B=.00000007 and: X=A*B takes 6.45 ms to multiply the floating point numbers Then changing line10 to A=1234.56789::B=987654.321 and: X=A*B takes 8.65 ms for each multiplication. So fewer digits can lead to a slight speed increase, but the lack of a decimal point makes no difference. Edited September 29, 2014 by senior_falcon 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted September 29, 2014 Share Posted September 29, 2014 Makes sense to me. That's one of the reasons the BBC B's basic was so fast. You could declare integer variables (by appending a % to the variable name). It went like hell. TIB/XB sucks so much I just can't go near it these days! It's a straight-jacket. Though, in its defence, it has great file handling support. It's great that they built that into the language, but they left out other equally useful things, such as types. That would have been awesome. Bah! Bloody TI! Quote Link to comment Share on other sites More sharing options...
TheMole Posted September 29, 2014 Share Posted September 29, 2014 TIB/XB sucks so much I just can't go near it these days! It's a straight-jacket. Though, in its defence, it has great file handling support. It's great that they built that into the language, but they left out other equally useful things, such as types. That would have been awesome. Indeed, I've been wondering how hard it would be to write an Extended Basic interpreter in assembler (or C, in my case) that adds goodies such as type support and the ability to run programs without using VDP RAM. Quote Link to comment Share on other sites More sharing options...
RXB Posted September 30, 2014 Share Posted September 30, 2014 (edited) Indeed, I've been wondering how hard it would be to write an Extended Basic interpreter in assembler (or C, in my case) that adds goodies such as type support and the ability to run programs without using VDP RAM. XB only uses VDP for File access, String Variables and Buffers. But areas like >03C0 and >0820 are XB buffers used for various routines and that is not counting VDP value stack routines of PUSH and POP that are called all the time for many subprograms. [0208] *********************************************************** [0209] * VDP addresses [0210] 02E2 NLNADD EQU >02E2 New LiNe ADDress [0211] 02FE ENDSCR EQU >02FE END of SCReen address [0212] 0371 LODFLG EQU >0371 Auto-boot needed flag [0213] 0372 START EQU >0372 Line to start execution at [0214] 0376 SYMBOL EQU >0376 Saved symbol table pointer [0215] 0382 SPGMPT EQU >0382 Saved PGMPTR for continue [0216] 0384 SBUFLV EQU >0384 Saved BUFLEV for contiue [0217] 0386 SEXTRM EQU >0386 Saved EXTRAM for continue [0218] * SAVEVP EQU >0388 Saved VSPRT for continue [0219] * ERRLN EQU >038A On-error line pointer [0220] 038C BUFSRT EQU >038C Edit recall start addr (VARW) [0221] 038E BUFEND EQU >038E Edit recall end addr (VARA) [0222] 0392 TABSAV EQU >0392 Saved main symbol table ponte [0223] 0396 SLSUBP EQU >0396 Saved LSUBP for continue [0224] 0398 SFLAG EQU >0398 Saved on-warning/break bits [0225] 039A SSTEMP EQU >039A To save subprogram program ta [0226] 039C SSTMP2 EQU >039C Same as above. Used in SUBPRO [0227] 039E MRGPAB EQU >039E MERGEd temporary for pab ptr [0228] *---------------------------------------------------------- [0229] * Added 6/8/81 for NOPSCAN feature [0230] 03B7 PSCFG EQU >03B7 [0231] *---------------------------------------------------------- [0232] * Flag 0: 99/4 console, 5/29/81 [0233] * 1: 99/4A console [0234] 03BB CONFLG EQU >03BB [0235] *---------------------------------------------------------- [0236] * Temporary [0237] 0374 NOTONE EQU >0374 NO-TONE for SIZE in ACCEPT us [0238] * in FLMGRS (4 bytes used) [0239] 0388 SAVEVP EQU >0388 [0240] 038A ERRLN EQU >038A [0241] 03AC ACCVRW EQU >03AC Temoporary used in ERRZZ, als [0242] * used in FLMGRS [0243] 03B0 VALIDP EQU >03B0 Use as two values passing fro [0244] 03B2 VALIDL EQU >03B2 VALIDATE code to READL1 [0245] 03BC OLDTOP EQU >03BC Temporary used in ERRZZ, also [0246] 0820 CRNBUF EQU >0820 CRuNch BUFfer address [0247] 08BE CRNEND EQU >08BE CRuNch buffer END [0248] 08C0 RECBUF EQU >08C0 Edit RECall BUFfer [0249] 0958 VRAMVS EQU >0958 Default base of value stack [0250] 0390 CNSTMP EQU >0390 Use as temporary stored place [0251] 03C0 VROAZ EQU >03C0 Temporary VDP Roll Out Area [0252] *********************************************************** I think that VDP Stack routines and these VDP buffer areas are mostly the slow down for XB. Every single access to VDP shows down XB and way to much is dependent on VDP. GPL is much slower then Assembly but if XB was pure Assembly it would be more than 1Meg in size. Edited September 30, 2014 by RXB Quote Link to comment Share on other sites More sharing options...
+Ksarul Posted September 30, 2014 Share Posted September 30, 2014 I wouldn't say that on the size, Rich--Myarc's Advanced BASIC for the Geneve isn't over 128K. . .and you can get 4 times that into one of the 512K cartridges. . .so it would be doable, but it would also be difficult, due to the need for the banking to get to routines. . . 1 Quote Link to comment Share on other sites More sharing options...
Gazoo Posted September 30, 2014 Share Posted September 30, 2014 I wouldn't say that on the size, Rich--Myarc's Advanced BASIC for the Geneve isn't over 128K. . .and you can get 4 times that into one of the 512K cartridges. . .so it would be doable, but it would also be difficult, due to the need for the banking to get to routines. . . I've actually had the idea to make a Rom cart with Myarc Advanced Basic, and yes it would be quite a project. I don't know if the TI source is available, but the Geneve source is. Maybe some sort of insane person might be amenable to teaming up for the project. Gazoo Quote Link to comment Share on other sites More sharing options...
JCCyC Posted September 30, 2014 Author Share Posted September 30, 2014 I've actually had the idea to make a Rom cart with Myarc Advanced Basic, and yes it would be quite a project. I don't know if the TI source is available, but the Geneve source is. Maybe some sort of insane person might be amenable to teaming up for the project. Gazoo I can help you if I can clone myself. I'll leave my clone doing all the drudgery and just fudge with retro stuff all day. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.