Universal BASIC stub...help?

Basic and Machine Language

Moderator: Moderators

Post Reply
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Universal BASIC stub...help?

Post by IsaacKuo »

I've written a generic BASIC stub which works regardless of memory expansions. The BASIC portion is:

Code: Select all

2010 SYS21+256*PEEK(44)
The start of the machine language portion copies the program to the default unexpanded VIC location (4096+), and then it resets various pointers to the default unexpanded VIC settings.

However, I'm concerned that not all the necessary pointers may be set as necessary...I don't modify any variable pointer settings, nor do I change the "next line" pointer...

http://members.cox.net/mechdan/vic/genstub.asm

Here's the code:

Code: Select all

; public domain generalized BASIC stub by Isaac Kuo
;
; Written for dasm; generate with command:
;
; dasm genstub.asm -ogenstub.prg

  processor 6502

  .org $1001   ; for the unexpanded Vic-20 by default

basic:         ; The BASIC line:            2010 SYS21+256*PEEK(44)
  .word zeros  ; link to next line
  .word 2010   ; line number
  .byte $9E    ; SYS token

  .byte $30 + (start%256)%100/10  ; offset digit
  .byte $30 + (start%256)%10      ; offset digit
  .byte $aa                       ; + token
  .byte "2","5","6"               ; 256
  .byte $ac                       ; * token
  .byte $c2                       ; PEEK token
  .byte "(","4","4",")"           ; (44) points to start of BASIC

zeros:
  .byte 0,0,0
; - - - - - - - - - - - - - - - - - - - - - - - - - - -

SCREEN = 7680
WORKFROM = 78
WORKTO   = WORKFROM+2

; - - - - - - - - - - - - - - - - - - - - - - - - - - -

start:

; copy code to desired address

  ldy #0
  lda 44
  sty WORKFROM
  sta WORKFROM+1   ; set source address
  lda #>basic
  sty WORKTO
  sta WORKTO+1     ; set destination address
copyloop:
  lda (WORKFROM),y
  sta (WORKTO),y
  iny
  bne copyloop     ; relative address branch
  inc WORKFROM+1
  inc WORKTO+1
  lda #>endmain+1
  cmp WORKTO+1
  beq endloop      ; relative address branch
  jmp copyloop     ; ABSOLUTE address jmp to the copied code...so you don't
                   ; overwrite the copying code while running it...
                   ; think about what happens if you're on an 8K+ VIC...
endloop:
; set various pointers to desired settings

  lda #>basic
  sta 44       ; set start of BASIC (to 4097)
  lda #>SCREEN
  sta 56       ; set limit of BASIC (to 7680)
  sta $0288    ; set software screen (to 7680)
  lda #150
  sta 36866    ; set hardware screen to 7680, 22 cols --HARDCODED--
  lda #240
  sta 36869    ; set hardware screen to 7680, font to default
 
  jmp beginmain    ; ABSOLUTE address jmp to routine at desired copied location

beginmain:

; ---> INSERT CODE HERE <---

endmain:
  rts
Does anyone see any flaws, or ways to make it more compact?

Note that after the first 256 bytes copied, the code branches to the COPY of the code. Without this, then there's a risk of overwriting the looping code...not good...

[edit: changed title from "generic" to "universal", to better convey the idea]
Last edited by IsaacKuo on Wed Sep 23, 2009 6:12 am, edited 1 time in total.
carlsson
Class of '6502
Posts: 5516
Joined: Wed Mar 10, 2004 1:41 am

Post by carlsson »

I think you want to JSR $C659 and perhaps JMP $C7AE if you intend to run a Basic program.

Anyway, I may be tired but do you copy from $2C00 to $1000, limiting yourself to the size of your header? The JMP to beginmain will be an absolute one, location depends on at what address you assemble the program. I think you want to have a look at the "rorg" directive.
Anders Carlsson

Image Image Image Image Image
carlsson
Class of '6502
Posts: 5516
Joined: Wed Mar 10, 2004 1:41 am

Post by carlsson »

By the way, you should consider using the $02A1 and $033C areas for helper routines. Ideally you want a program you can LOAD"FILE",8 and it will automatically reconfigure itself to unexpanded mode no matter if your VIC already was unexpanded or expanded by any amount of memory.
Anders Carlsson

Image Image Image Image Image
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Post by IsaacKuo »

carlsson wrote:By the way, you should consider using the $02A1 and $033C areas for helper routines. Ideally you want a program you can LOAD"FILE",8 and it will automatically reconfigure itself to unexpanded mode no matter if your VIC already was unexpanded or expanded by any amount of memory.
That's the whole idea.

It figures out where it was loaded to use PEEK(44). 1+256*PEEK(44) is start of BASIC.

The idea is to first copy the program from where it is to where it would be if the VIC were unexpanded. After doing this copy, it modifies various pointers and the video hardware into the stock unexpanded configuration.

That's why I have to be careful about relative branches vs absolute JMP's. Here's the basic idea:

1) First the BASIC stub does a SYS to the current location of the ML program.

2) The first 256 bytes of the program are copied to the desired location (using a relative branch).

3) An absolute JMP continues jumps to the copy.

4) The copy loop continues to the end of the program.

5) An absolute JMP goes to the start of the copied program (this is redundant if the program is bigger than 256 bytes, but it's necessary if the program is smaller than 256 bytes).

This code should work for programs up to 3.5K in size, if the VIC20 is either stock or has 8+K expansions.

However, if the VIC20 has a 3K expansion, the program must be at most 3K in size. If the program is greater than 3K in size, then the final .5K will immediately get overwritten at the start. The copy routine starts from the bottom and goes to the top. I did it this way for maximum compatibility with 8+K expansions, although it introduces a limitation for 3K expansion.

Of course, if you use a custom font at 7168, then you're already limited to 3K program size. So no big deal, I think.
tlr
Vic 20 Nerd
Posts: 567
Joined: Mon Oct 04, 2004 10:53 am

Re: Generic BASIC stub...help?

Post by tlr »

IsaacKuo wrote:Does anyone see any flaws, or ways to make it more compact?
Applying some tweaks saves another 11 bytes:

Code: Select all

start:
; copy code to desired address

  ldy #0
  ldx #>(endmain-basic+$ff)
copyloop:
  lda (43),y
wt_sm:
  sta basic,y
  iny
  bne copyloop     ; relative address branch
  inc 44
  inc wt_sm+2
  dex
  beq endloop      ; relative address branch
  jmp copyloop     ; ABSOLUTE address jmp to the copied code...so you don't
                   ; overwrite the copying code while running it...
                   ; think about what happens if you're on an 8K+ VIC...
endloop:
; set various pointers to desired settings
; (44 gets reset below)
To handle both up and down copying it will have to be a bit longer.
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Post by IsaacKuo »

I'm worried about the self modifying code. What happens when the user attempts to RUN the program a second time? By then, the contents of wt_sm+2 will be out of whack, right?
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Post by IsaacKuo »

I've made extensive modifications:

Code: Select all

; public domain generalized BASIC stub by Isaac Kuo
;
; Written for dasm; generate with command:
;
; dasm genstub.asm -ogenstub.prg

  processor 6502

  .org $1001   ; for the unexpanded Vic-20 by default

basic:         ; The BASIC line:            2010 SYS21+256*PEEK(44)
  .word zeros  ; link to next line
  .word 2010   ; line number
  .byte $9E    ; SYS token

  .byte $30 + (start%256)%100/10  ; offset digit
  .byte $30 + (start%256)%10      ; offset digit
  .byte $aa                       ; + token
  .byte "2","5","6"               ; 256
  .byte $ac                       ; * token
  .byte $c2                       ; PEEK token
  .byte "(","4","4",")"           ; (44) points to start of BASIC

zeros:
  .byte 0,0,0
; - - - - - - - - - - - - - - - - - - - - - - - - - - -

SCREEN = 7680
STARTOFBASIC = 43
WORKTO   = 78

; - - - - - - - - - - - - - - - - - - - - - - - - - - -

start:

; copy code to desired address

  ldy STARTOFBASIC
  lda #>basic
  sty WORKTO
  sta WORKTO+1          ; set destination address
outerloop:
  bmi endloop           ; keep on copying until you get to 32768
innerloop:
  lda (STARTOFBASIC),y
  sta (WORKTO),y
  iny
  bne innerloop         ; relative address branch
  inc STARTOFBASIC+1
  inc WORKTO+1
  jmp outerloop         ; ABSOLUTE jmp to go to the copy

endloop:
; set various pointers to desired settings

  lda #>basic
  sta STARTOFBASIC+1    ; set start of BASIC (to 4097)
  sta 62                ; change BASIC current statement pointer -- MAYBE NOT RIGHT?
  lda #>SCREEN
  sta 56                ; set limit of BASIC (to 7680)
  sta $0288             ; set software screen (to 7680)

  jsr $C533             ; Rebuild BASIC text link pointers

  jsr $E518             ; Initalize input/output - like RUN/STOP + RESTORE.

;  lda #150
;  sta 36866             ; set hardware screen to 7680, 22 cols --HARDCODED--
;  lda #240
;  sta 36869             ; set hardware screen to 7680, font to default

beginmain:

; ---> INSERT YOUR CODE HERE <---

endmain:
  rts
It currently just dumbly copies bytes all the way to 32768...costs a few seconds to save a few bytes of loop logic.

I also saved a few bytes by using the "runstop restore" routine rather than manually setting the video registers.

But there are still some problems. It works fine on an unexpanded or 3K expanded VIC. However, for an 8+K expanded VIC something bad happens at some point. I suspect it fails to cleanly exit the program after returning to BASIC (completion of the SYS call).

After returning to interactive BASIC, many statements give SYNTAX ERROR--including the RUN command itself! In other words, the program works only once. Subsequent attempts to RUN the program fail on 8+K expanded VICs.

I'd like to execute some other BASIC ROM routines to reset the variable pointers and to cleanly modify the "current command" pointer to get it to exit cleanly after the program completes. But I don't know the BASIC ROM well enough...the following routine looks promising:
$C659: Reset execution to start of program and perform CLR (set pointers to variables).
However, I do NOT want the execution to remain at the start of the program--that would result in an infinite loop! I'd like the program to cleanly exit to interactive BASIC after completion.

Any ideas?

Thanks!
rhurst
Omega Star Commander
Posts: 1371
Joined: Thu Jan 31, 2008 2:12 pm
Website: https://robert.hurst-ri.us
Location: Providence, RI
Occupation: Tech & Innovation

Post by rhurst »

Without reviewing any of the code, would:

endmain: JMP $E467 ; BASIC Warm Restart [RUNSTOP-RESTORE]

... instead of RTS be of any help?
Any technology distinguishable from magic is insufficiently advanced.
https://robert.hurst-ri.us/rob/retrocomputing
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Post by IsaacKuo »

I tried replacing the bottom "rts" with "jmp $e467". I still get the ?SYNTAX ERROR behavior, so I guess it's not caused by anything strange after the "rts" back to the BASIC program.

Still, this lets me use the $C659 routine to perform a CLR without starting an infinite loop...

Here's my current version:

Code: Select all

; public domain generalized BASIC stub by Isaac Kuo
;
; Written for dasm; generate with command:
;
; dasm genstub.asm -ogenstub.prg

  processor 6502

  .org $1001   ; for the unexpanded Vic-20 by default

basic:         ; The BASIC line:            2010 SYS21+256*PEEK(44)
  .word zeros  ; link to next line
  .word 2010   ; line number
  .byte $9E    ; SYS token

  .byte $30 + (start%256)%100/10  ; offset digit
  .byte $30 + (start%256)%10      ; offset digit
  .byte $aa                       ; + token
  .byte "2","5","6"               ; 256
  .byte $ac                       ; * token
  .byte $c2                       ; PEEK token
  .byte "(","4","4",")"           ; (44) points to start of BASIC

zeros:
  .byte 0,0,0
; - - - - - - - - - - - - - - - - - - - - - - - - - - -

SCREEN = 7680
STARTOFBASIC = 43
WORKTO   = 78

; - - - - - - - - - - - - - - - - - - - - - - - - - - -

start:

; copy code to desired address

  ldy STARTOFBASIC      ; loads y with 1, so the first byte will be skipped...it's okay because of jsr $C533
  sty WORKTO
  ldx #>basic
outerloop:
  stx WORKTO+1          ; set destination address
  cpx #>endmain+1
  beq endloop
innerloop:
  lda (STARTOFBASIC),y
  sta (WORKTO),y
  iny
  bne innerloop         ; relative address branch
  inc STARTOFBASIC+1
  inx
  jmp outerloop         ; ABSOLUTE jmp to go to the copy

endloop:
; set various pointers to desired settings

  lda #>basic
  sta STARTOFBASIC+1    ; set start of BASIC (to 4097)
  sta 46                ; set start of variables
  lda #>SCREEN
  sta 56                ; set limit of BASIC (to 7680)
  sta $0288             ; set software screen (to 7680)

  jsr $C533             ; Rebuild BASIC text link pointers
  jsr $C659             ; Reset execution to start of program and perform CLR

  jsr $E518             ; Initalize input/output - like RUN/STOP + RESTORE.
;  lda #150
;  sta 36866             ; set hardware screen to 7680, 22 cols
;  lda #240
;  sta 36869             ; set hardware screen to 7680, font to default

beginmain:

; ---> INSERT YOUR CODE HERE <---

endmain:
  jmp $E467              ; BASIC Warm Restart [RUNSTOP-RESTORE] 
As before, it works fine for unexpanded and 3K expanded, but it only works on the first RUN for 8K expanded. Subsequent attempts to RUN result in ?SYNTAX ERROR...

I have no idea what's causing the ?SYNTAX ERROR.
IsaacKuo
Vic 20 Hobbyist
Posts: 147
Joined: Tue Aug 04, 2009 5:45 am

Post by IsaacKuo »

SUCCESS!!!

I figured out the cause of the bug. It turns out that the contents of location 4096 need to be 0. With an 8K+ expansion, the contents of 4096 will be 46 (the "*" in the upper left corner of the splash screen). This causes a ?SYNTAX error when you try to run the program.

Here's the current version of the universal BASIC stub:

Code: Select all

; public domain generalized BASIC stub by Isaac Kuo
;
; Written for dasm; generate with command:
;
; dasm genstub.asm -ogenstub.prg

  processor 6502

  .org $1001   ; for the unexpanded Vic-20 by default

basic:         ; The BASIC line:            2010 SYS21+256*PEEK(44)
  .word zeros  ; link to next line
  .word 2010   ; line number
  .byte $9E    ; SYS token

  .byte $30 + (start%256)%100/10  ; offset digit
  .byte $30 + (start%256)%10      ; offset digit
  .byte $aa                       ; + token
  .byte "2","5","6"               ; 256
  .byte $ac                       ; * token
  .byte $c2                       ; PEEK token
  .byte "(","4","4",")"           ; (44) points to start of BASIC

zeros:
  .byte 0,0,0
; - - - - - - - - - - - - - - - - - - - - - - - - - - -

SCREEN = 7680
STARTOFBASIC = 43
WORKTO   = 78

; - - - - - - - - - - - - - - - - - - - - - - - - - - -

start:

; copy code to desired address

  ldy STARTOFBASIC      ; initializes y to 1, which is okay due to Rebuild BASIC text link pointers later on
  sty WORKTO
  ldx #>basic
outerloop:
  stx WORKTO+1          ; set destination address
  cpx #>endmain+1
  beq endloop
innerloop:
  lda (STARTOFBASIC),y
  sta (WORKTO),y
  iny
  bne innerloop         ; relative address branch
  sty basic-1           ; poke 4096,0 -- THIS IS NEEDED TO PREVENT ?SYNTAX ERROR ON SUBSEQUENT RUN's
  inc STARTOFBASIC+1
  inx
  jmp outerloop         ; ABSOLUTE jmp to go to the copy

endloop:
; set various pointers to desired settings

  lda #>basic
  sta STARTOFBASIC+1    ; set start of BASIC (to 4097)
;;;;;;;;  sta 46                ; set start of variables (optional)
  lda #>SCREEN
  sta $0288             ; set software screen (to 7680)
  sta 56                ; set limit of BASIC (to 7680)

  jsr $C533             ; Rebuild BASIC text link pointers
;;;;;;;;  jsr $C659             ; Reset execution to start of program and perform CLR (optional)


; - - - - - - - - - - - - - - - - - - - - - - - - - - -
; set the video hardware, either using jsr $E518 for the default settings
; or by manually setting the video registers for custom settings

  jsr $E518             ; Initalize input/output - like RUN/STOP + RESTORE.
;  lda #150
;  sta 36866             ; set hardware screen to 7680, 22 cols
;  lda #240
;  sta 36869             ; set hardware screen to 7680, font to default
; - - - - - - - - - - - - - - - - - - - - - - - - - - -

beginmain:

; ---> INSERT YOUR CODE HERE <---

endmain:
  jmp $E467              ; BASIC Warm Restart [RUNSTOP-RESTORE]
http://members.cox.net/mechdan/vic/genstub.asm
Post Reply