6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Sep 30, 2024 6:33 am

All times are UTC




Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Jan 14, 2015 8:29 am 
Offline

Joined: Mon Jan 07, 2013 2:42 pm
Posts: 576
Location: Just outside Berlin, Germany
Sorry if this has been posted already (didn't see it anywhere else, though), Pagetable has posted the source of Microsoft Basic 1978, with comments and their analysis. They claim this is the oldest publicly known software that Bill Gates worked on.

http://www.pagetable.com/?p=774

Funny easter egg included:

As has been discussed before, this code writes the string “MICROSOFT!” into the PET’s screen RAM if the argument to WAIT is “6502″. The encoded string is hidden as two extra 40 bit floating point numbers appended to the coefficients used by the SIN function:


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 30, 2017 5:31 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
I always thought that the CHRGET routine:
Code:
CHRGET: INC     CHRGET+7        ;INCREMENT THE WHOLE TXTPTR.
        BNE     CHRGOT
        INC     CHRGET+8
CHRGOT: LDA     60000           ;A LOAD WITH AN EXT ADDR.
TXTPTR= CHRGOT+1
        CMPI    " "             ;SKIP SPACES.
        BEQ     CHRGET
QNUM:   CMPI    ":"             ;IS IT A ":"?
        BCS     CHRRTS          ;IT IS .GE. ":"
        SEC
        SBCI    "0"             ;ALL CHARS .GT. "9" HAVE RET'D SO
        SEC
        SBCI    256-"0"         ;SEE IF NUMERIC.
                                ;TURN CARRY ON IF NUMERIC.
                                ;ALSO, SETZ IF NULL.
CHRRTS: RTS                     ;RETURN TO CALLER.

... could have been written better, but I never tried to improve it until now (about 40 years too late, it seems). It appears that a non-space character retrieved should arrive unmolested, but the C flag should be clear for the ASCII range [1..47] and set otherwise. Additionally, the Z flag should be set for NUL and ":" and clear otherwise.
Code:
CHRGET: INC     CHRGET+7        ;INCREMENT THE WHOLE TXTPTR.
        BNE     CHRGOT
        INC     CHRGET+8
CHRGOT: LDA     60000           ;A LOAD WITH AN EXT ADDR.
TXTPTR= CHRGOT+1
        CMPI    " "             ;SKIP SPACES.
        BEQ     CHRGET
QNUM:   CMPI    ":"             ;IS IT A ":"?
        BCS     CHRRTS          ;IT IS .GE. ":"
        CMPI    "0"             ;ALL CHARS .GT. "9" HAVE RET'D SO
                                ;TURN CARRY ON IF NUMERIC.
        ANDI    255             ;ALSO, SETZ IF NULL.
CHRRTS: RTS                     ;RETURN TO CALLER.

Didn't I just save two bytes (and four cycles for chars < ":")? CHRGET is heavily used during run-time, and I'm willing to speculate that these improvements would be noticeable. In my personal experience, 65xx MS BASIC with 40-bit floats was already significantly snappier on the 1 MHz Apple ][ than it was with 32-bit floats on the 1.774 MHz TRS-80 Model 1 ... could Mr. Gates have been secretly sand-bagging, or was he just in such a hurry to deliver that he failed to notice?

Thoughts and/or corrections?

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 30, 2017 5:49 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
(Bear in mind the Z80's memory bus is such that it performs 2x, or 2.3x, slower than a 6502 at the same clock speed. So the TRS-80 at 1.774MHz is handicapped relative to a 1MHz 6502, on average. Of course the factor is a broad approximation, and the design of each system and software will fuzz the comparison.)


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 30, 2017 5:53 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
In addition, the Applesoft tokenizer removed spaces from everything that wasn't a literal string, REMark or DATA statement (adding its own spaces in rather clumsily while LISTing). So the CMP " " and BEQ CHRGET could have been removed as well, saving four more bytes and four more cycles for every fetch, giving the Apple ][ an even greater speed advantage.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 30, 2017 10:45 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
EhBASIC has a similar CHRGET routine:
Code:
; character get subroutine for zero page

; For a 1.8432MHz 6502 including the JSR and RTS
; fastest (>=":") =  29 cycles =  15.7uS
; slowest (<":")  =  40 cycles =  21.7uS
; space skip      = +21 cycles = +11.4uS
; inc across page =  +4 cycles =  +2.2uS

; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the
; block is copied to it's destination, any non zero page address will do at assembly
; time, to assemble a three byte instruction.
; ...
LAB_IGBY          = $BC       ; get next BASIC byte subroutine

LAB_GBYT          = $C2       ; get current BASIC byte subroutine
Bpntrl            = $C3       ; BASIC execute (get byte) pointer low byte
Bpntrh            = Bpntrl+1  ; BASIC execute (get byte) pointer high byte
; ...

; page 0 initialisation table from $BC
; increment and scan memory

LAB_2CEE
      INC   Bpntrl            ; increment BASIC execute pointer low byte
      BNE   LAB_2CF4          ; branch if no carry
                              ; else
      INC   Bpntrh            ; increment BASIC execute pointer high byte

; page 0 initialisation table from $C2
; scan memory

LAB_2CF4
      LDA   $FFFF             ; get byte to scan (addr set by call routine)
      CMP   #TK_ELSE          ; compare with the token for ELSE
      BEQ   LAB_2D05          ; exit if ELSE, not numeric, carry set

      CMP   #':'              ; compare with ":"
      BCS   LAB_2D05          ; exit if >= ":", not numeric, carry set

      CMP   #' '              ; compare with " "
      BEQ   LAB_2CEE          ; if " " go do next

      SEC                     ; set carry for SBC
      SBC   #'0'              ; subtract "0"
      SEC                     ; set carry for SBC
      SBC   #$D0              ; subtract -"0"
                              ; clear carry if byte = "0"-"9"
LAB_2D05
      RTS
The last comment correctly states that the carry on RTS is clear for a numeric character. This comment is wrong in the original MS-BASIC source. The detour through the 2 SBCs is necesssary (and quite clever) to clear the carry instead of setting it!

_________________
6502 sources on GitHub: https://github.com/Klaus2m5


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 30, 2017 11:31 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
Quote:
Paul Allen’s additional macros for 6502 development made the MACRO-10 assembler output one 36 bit PDP-10 instruction word for every 6502 byte. When targeting a real 6502 machine, the 6502 binary could be created by simply extracting one byte from every PDP-10 word.

In the case of targeting the simulator, the code created by the assembler could just be run without modification, since every emitted PDP-10 instruction was constructed so that it would trap – the linked-in simulator would then extract the 6502 opcode from the instruction and emulate the 6502 behavior.

How devilishly clever.


Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 18, 2017 12:26 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
barrym95838 wrote:
In addition, the Applesoft tokenizer removed spaces from everything that wasn't a literal string, REMark or DATA statement (adding its own spaces in rather clumsily while LISTing). So the CMP " " and BEQ CHRGET could have been removed as well, saving four more bytes and four more cycles for every fetch, giving the Apple ][ an even greater speed advantage.

Mike B.

I tried a modified CHRGET on AppleWin, and found no perceptible difference from saving five bytes and six cycles.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 21, 2017 9:14 pm 
Offline

Joined: Wed Mar 02, 2016 12:00 pm
Posts: 343
Funny thing..., the only Microsoft software to ever run on Apple computers were MS Basic and MS Office.. Or am I wrong?


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 21, 2017 9:22 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
Do you count PowerPoint, shipped in '87 for Mac and the company acquired later the same year by MS?
Quote:
The Microsoft Corporation announced its first significant software acquisition today, paying $14 million for Forethought Inc. of Sunnyvale, Calif. Forethought makes a program called PowerPoint that allows users of Apple Macintosh computers to make overhead transparencies or flip charts.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 22, 2017 12:19 am 
Offline

Joined: Tue Oct 24, 2017 1:08 am
Posts: 10
kakemoms wrote:
Funny thing..., the only Microsoft software to ever run on Apple computers were MS Basic and MS Office.. Or am I wrong?


There was at least one other thing. Multiplan, Microsoft's competition to VisiCalc, ran on Apple ][.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 22, 2017 8:18 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
(Welcome to the forums, Uncle Warthog!)


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 02, 2019 7:15 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Klaus2m5 wrote:
EhBASIC has a similar CHRGET routine:
Code:
; character get subroutine for zero page

; For a 1.8432MHz 6502 including the JSR and RTS
; fastest (>=":") =  29 cycles =  15.7uS
; slowest (<":")  =  40 cycles =  21.7uS
; space skip      = +21 cycles = +11.4uS
; inc across page =  +4 cycles =  +2.2uS

; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the
; block is copied to it's destination, any non zero page address will do at assembly
; time, to assemble a three byte instruction.
; ...
LAB_IGBY          = $BC       ; get next BASIC byte subroutine

LAB_GBYT          = $C2       ; get current BASIC byte subroutine
Bpntrl            = $C3       ; BASIC execute (get byte) pointer low byte
Bpntrh            = Bpntrl+1  ; BASIC execute (get byte) pointer high byte
; ...

; page 0 initialisation table from $BC
; increment and scan memory

LAB_2CEE
      INC   Bpntrl            ; increment BASIC execute pointer low byte
      BNE   LAB_2CF4          ; branch if no carry
                              ; else
      INC   Bpntrh            ; increment BASIC execute pointer high byte

; page 0 initialisation table from $C2
; scan memory

LAB_2CF4
      LDA   $FFFF             ; get byte to scan (addr set by call routine)
      CMP   #TK_ELSE          ; compare with the token for ELSE
      BEQ   LAB_2D05          ; exit if ELSE, not numeric, carry set

      CMP   #':'              ; compare with ":"
      BCS   LAB_2D05          ; exit if >= ":", not numeric, carry set

      CMP   #' '              ; compare with " "
      BEQ   LAB_2CEE          ; if " " go do next

      SEC                     ; set carry for SBC
      SBC   #'0'              ; subtract "0"
      SEC                     ; set carry for SBC
      SBC   #$D0              ; subtract -"0"
                              ; clear carry if byte = "0"-"9"
LAB_2D05
      RTS
The last comment correctly states that the carry on RTS is clear for a numeric character. This comment is wrong in the original MS-BASIC source. The detour through the 2 SBCs is necesssary (and quite clever) to clear the carry instead of setting it!


Okay, how about replacing:
Code:
      ...
      SEC                     ; set carry for SBC
      SBC   #'0'              ; subtract "0"
      SEC                     ; set carry for SBC
      SBC   #$D0              ; subtract -"0"
                              ; clear carry if byte = "0"-"9"
      ...
with:
Code:
      ...
      EOR   #'0'              ; convert to binary
      CMP   #10               ; clear carry if decimal digit
      EOR   #'0'              ; convert back to ASCII
      ...
Only a two-cycle improvement (if I'm not horribly mistaken), but still ... ;-)

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 02, 2019 1:21 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
And it has the virtue of working even if characters above '9' haven't already been eliminated.


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 07, 2019 3:13 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1383
I did a CMOS only version of EhBasic a while back... which does seem to run a tad faster. I also moved the CHRGET (equivalent) routine into ROM and used a CMOS addressing mode to get the current byte. Granted, that instruction takes one additional clock cycle but it saves a lot of page zero space. Here's the code snippet:

Code:
; character get subroutine - ROM based

; the target address for the LDA at LAB_GBYT is the BASIC execute pointer.
; block is no longer copied to page zero and uses a CMOS instruction/address mode.
; 16-bit pointer is located in page zero.

; increment and scan memory
LAB_IGBY
      INC   Bpntrl            ; increment BASIC execute pointer low byte
      BNE   LAB_GBYT          ; branch if no carry, else
      INC   Bpntrh            ; increment BASIC execute pointer high byte

; scan memory

LAB_GBYT
      LDA   (Bpntrl)          ; get byte to scan (addr set by call routine)
      CMP   #TK_ELSE          ; compare with the token for ELSE
      BEQ   LAB_2D05          ; exit if ELSE, not numeric, carry set

      CMP   #':'              ; compare with ":"
      BCS   LAB_2D05          ; exit if >= ":", not numeric, carry set

      CMP   #' '              ; compare with " "
      BEQ   LAB_IGBY          ; if " " go do next

      SEC                     ; set carry for SBC
      SBC   #'0'              ; subtract "0"
      SEC                     ; set carry for SBC
      SBC   #$D0              ; subtract -"0"
                              ; clear carry if byte = "0"-"9"
LAB_2D05
      RTS


I'm guessing that moving the CMP #' ' ahead of the CMP#: might be a bit quicker on code execution, but depends on how the source code is formatted. In any case, I can make Mike's changes at the bottom of the code and give it a go on some long running programs for testing. One would think that optimizing this routine would make BASIC run faster, but it depends on how long the program takes to run and also, to some degree, how many spaces are in the program.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 07, 2019 4:58 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1383
I made a few changes to the CHRGET routine in EhBasic and found some interesting timing changes. I'm using Bill O's Mandelbrot program from an earlier post. I've added a couple CALL statements which reset and display my benchmark counter (part of my Monitor/BIOS). The stock CHRGET routine results in a timing of 113.74 seconds to compete one run of the Mandelbrot. BASIC program:

Code:
5 CALL 65328
10 MX=20
20 LET C$=" .,'~!^:;[/<&?oxOX#  "
30 FOR Y=-19 TO 19
40 FOR X=-49 TO 30
50 CR=X/30
70 CI=Y/15
80 ZR = CR
90 ZI = CI
95 CO = 1
100 ZM = ZR*ZR
105 ZN = ZI*ZI
107 ZL = ZM+ZN
110 IF ZL>4 THEN GOTO 170
120 ZJ=ZM-ZN+CR
130 ZI=ZR*ZI*2+CI
140 ZR=ZJ
150 CO=CO+1
160 IF CO<MX THEN GOTO 100
170 PRINT MID$(C$,1+COUNT,1);
180 NEXT X
185 PRINT ""
190 NEXT Y
195 CALL 57371
200 GOTO 5


First, I moved the CMP #' ' space to the top after the LDA, like this:

Code:
LAB_GBYT
      LDA   (Bpntrl)          ; get byte to scan (addr set by call routine)

      CMP   #' '              ; compare with " "
      BEQ   LAB_IGBY          ; if " " go do next

      CMP   #TK_ELSE          ; compare with the token for ELSE
      BEQ   LAB_2D05          ; exit if ELSE, not numeric, carry set

      CMP   #':'              ; compare with ":"
      BCS   LAB_2D05          ; exit if >= ":", not numeric, carry set


The timing changed to 114.90 seconds, over a second longer per run. Next, I changed to:

Code:
LAB_GBYT
      LDA   (Bpntrl)          ; get byte to scan (addr set by call routine)

      CMP   #TK_ELSE          ; compare with the token for ELSE
      BEQ   LAB_2D05          ; exit if ELSE, not numeric, carry set

      CMP   #' '              ; compare with " "
      BEQ   LAB_IGBY          ; if " " go do next

      CMP   #':'              ; compare with ":"
      BCS   LAB_2D05          ; exit if >= ":", not numeric, carry set


The timing actually got worse, at 115.06 seconds! So I put it back to the original code. I then tried Mike's change to the lower section of code:

Quote:
LAB_GBYT
LDA (Bpntrl) ; get byte to scan (addr set by call routine)

CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set

CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set

CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next

; SEC ; set carry for SBC
; SBC #'0' ; subtract "0"
; SEC ; set carry for SBC
; SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"

; Change suggested by Mike Barry to replace above

EOR #'0' ; convert to binary
CMP #10 ; clear carry if decimal digit
EOR #'0' ; convert back to ASCII


This resulted in a timing of 113.54 seconds, a savings of 200 milliseconds per run. Nice!

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: