picocomputer / ehbasic-plus

Experimental Enhanced 6502 BASIC for the Picocomputer
2 stars 1 forks source link

Add graphics plotting support to EhBASIC #1

Closed netzerohero closed 10 months ago

netzerohero commented 11 months ago

With version-0.4 of the firmware completing all VGA modes, adding point and line plotting graphics to EhBASIC would open-up the picocomputer's EhBASIC to a wealth of BASIC code available from the retro-computer domain.

netzerohero commented 11 months ago

Graphics within EhBASIC can be incrementally added over time:

  1. first via basic's "CALL" hooks: The EhBASIC Reference Manual includes guidance (pgs 27-29) on how-to-add graphics point-plotting using the BASIC CALL command.
  2. then as others in the broader community are demonstrating (i.e., extending EhBASIC assembly with graphics commands); and,
  3. perhaps eventually using tgi - Tiny Graphics Interface - for graphics.
Hyenadae commented 11 months ago

Not sure if you've had any time to think or plan this out further, but what do you think about:

  1. The usage/mixture of C and Source files for extending BASIC functionality (ie, do we just re-link in C and hope the compiler-assembler ties things in despite BASIC taking over the whole machine, as said in the memory map thread)

  2. Should we try to re-implement loose versions of graphics functions from other BASICS, ie, Applesoft basic has the 'H' modes and syntax, for example:

https://rosettacode.org/wiki/Barnsley_fern#Applesoft_BASIC https://rosettacode.org/wiki/Archimedean_spiral#AmigaBASIC

HGR(1) (Hires low color, with terminal shown) on our EHBasic could re-use this config from Rumble's video: https://youtu.be/QVvH03OSVf0?t=855 https://youtu.be/PHfKCxjsmos?t=581

Mode 3, 320x140 (on 180-height canvas) with 255 colors, but with Mode 0 at 320x40 also enabled on the overlay canvas at the bottom for the last 40 pixels from the bottom-up

and HGR2 can just be the 'normal' 255 color bitmap mode3 without the 2nd plane for the terminal output?

I assume we'll need to modify the control-c / end BASIC program calls to always disable the graphics canvas/plane too. Then when HGR(2) is called, have it auto-clear the extended RAM and then continue? I'm slowly looking through how the Applesoft BASIC assembly does things, and it does seem to be obvious to put as much as heavily called color data (if possible) into the zero page lol.

Now, if we wanted to use VGA res monochrome, we'd have a 'small' problem that it'd need five (3 for height (up to 768 width) plus two for up to 512 height) bytes to address the screen position, rather than just three for a height of 320x240?

So for the mapping versus what we already have https://www.landsnail.com/a2ref.htm

Applesoft Assembly macros and ZP location: https://github.com/cmosher01/Apple-II-Source/blob/master/src/system/applesoft/applesoft.m4

HGR_DX              = $D0                           ; 
HGR_DY              = $D2                           ; 
HGR_QUADRANT        = $D3                           ; 
HGR_E               = $D4                           ; 

HGR_X               = $E0                           ; 
HGR_Y               = $E2                           ; 
HGR_COLOR           = $E4                           ; 
HGR_HORIZ           = $E5                           ; BYTE INDEX FROM GBASH,L
HGR_PAGE            = $E6                           ; HGR=$20, HGR2=$40
HGR_SCALE           = $E7                           ; 
HGR_SHAPE_PNTR      = $E8                           ; 
HGR_COLLISIONS      = $EA                           ; 

But our ZP.inc has a lot of definitions already (trying to fill in for assumed free locations)

;                 = $E2       ; unused (HGRX)
;                 = $E3       ; unused (HGRX)
;                 = $E4       ; unused (HGRY)
;                 = $E5       ; unused (HGRY)
;                 = $E6       ; unused (HGRDX)
;                 = $E7       ; unused (HGRDX)
;                 = $E8       ; unused (HGRDY)
;                 = $E9       ; unused (HGR_HORIZ)
;                 = $EA       ; unused (HGR_QUADRANT)
;                 = $EB       ; unused (HGR_SCALE)
;                 = $EC       ; unused (HGR_E)
;                 = $ED       ; unused (HGR_E)
;                 = $EE       ; unused (HGR_COLOR)

Bitmap_graphics.c/h                         EhBasic/AppleSoft Basic

erase_canvas (wipe XRAM)                GR / CLS                           
draw_pixe/draw_line (Color, X1,Y1 or X2,Y2)                 HPLOT(Needs` to support range input) & SCRN(x,y) 
draw_hline (Color, X,Y,Width)           HLIN x1,x2 at y
draw_vline (Color, X,Y,Height)          VLIN y1,y2 at x
init_bitmap_graphics                    HGR
HGR+mode0XReg                           HGR2
<ColorVariable, ZP?>                   HCOLOR (HiResColor) / COLOR=(X)
draw_circle/rect                        DRAW <shapeTableID> AT <X,Y> (Rarely used, low documentation)
???                                      XDRAW (?) 
fill_circle&helper                     N/A?

I assume we can save some cycles and bytes by only initially supporting one kind of hicolor bitmap mode, and also only one configuration of canvas/multi-plane to match HGR2, and/or the monochrome (640x480) high-res bitmap since we've got an 8Mhz 6502 (call it HGR0 or HGR3?)

I don't know if we have to compile all the functions and especially RIA/XREG init stuff to their assembly variations, and then shove them into their respective macros which are called by the BASIC environment? And what about VSYNC, does every graphical function need to care about it before writing, or can we just keep sending data and the Pico VGA will only refresh it the moment it needs to be written

I'm hopefully able to ship in a VGA -> USB capture device, so I can actually begin seeing whatever graphics are made while I'm on my desktop etc. I'm on vacation for three weeks, so I'm hoping I can learn enough to try to help out if possible

Hyenadae commented 11 months ago

On the "Mix of C" comment, I was skeptical myself and what I assume what could work is to find the simplest and smallest (instruction wise) assembled outputs of the most important originally-C based functions.

IE, what's the smallest amount of code that lets us do RIA.addr0 = addr; RIA.step0 = addr; RIA.rw0 = 0;

xreg_vga_canvas(canvas_mode); and xram0_struct_set(canvas_struct, vga_mode3_config_t, (init items), value)

plus a lot of functions from rp6502.h to actually write properly to the RIA/VGA system but hidden from the BASIC/user end

Hyenadae commented 11 months ago

Okay, so a decent 'basic' assembly code / ROM to be created (and not the .rp6502 version) from a C function to clear XRAM using pre-defined calls is:


#include <rp6502.h>
#include <stdio.h>
#include <stdint.h>

void main()
{
    //uint16_t i, num_bytes;
    //num_bytes = 57600; (320 times 180 res at 255 colors)
    uint16_t i;
    static uint16_t canvas_data = 0x0000;

    RIA.addr0 = canvas_data;
    RIA.step0 = 1;
    for (i = 0; i < (3600); i++) {
        // unrolled for speed
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
        RIA.rw0 = 0;
    }
}

From here, using: https://www.masswerk.at/6502/disassembler.html to load the ROM/RAM file and to disassemble.

* = $0000
.BYTE $23          ;%00100011 '#'
AND ($52,X)        ;(6)
BVC $003B          ;(2/3)
AND $30,X          ;(4)
.BYTE $32          ;%00110010 '2'
ASL A              ;(2)
BIT $30            ;(3)
.BYTE $32          ;%00110010 '2'
BMI $003E          ;(2/3)
JSR $3124          ;(6)
AND $42,X          ;(4)
JSR $4524          ;(6)
.BYTE $32          ;%00110010 '2'
.BYTE $42          ;%01000010 'B'
.BYTE $44          ;%01000100 'D'
EOR $41            ;(3)
AND ($39),Y        ;(5/6)
ASL A              ;(2)
LDX #$FF           ;(2)
TXS                ;(2)
CLD                ;(2)
LDA #$00           ;(2)
STA $00            ;(3)
LDA #$F7           ;(2)
STA $01            ;(3)
JSR $0311          ;(6)
JSR $02B7          ;(6)
JSR $022B          ;(6)
JSR $0237          ;(6)
.BYTE $DA          ;%11011010
PHA                ;(3)
JSR $02AB          ;(6)
PLA                ;(4)
STA $FFF4          ;(4)
.BYTE $FA          ;%11111010
STX $FFF6          ;(4)
LDA #$FF           ;(2)
STA $FFEF          ;(4)
.BYTE $DB          ;%11011011
LDY #$00           ;(2)
BEQ $0054          ;(2/3)
LDA #$37           ;(2)
LDX #$02           ;(2)
JMP $0336          ;(3)
RTS                ;(6)
JSR $02E4          ;(6)
LDA $0335          ;(4)
STA $FFE7          ;(4)
LDA $0334          ;(4)
STA $FFE6          ;(4)
LDA #$01           ;(2)
STA $FFE5          ;(4)
LDX #$00           ;(2)
TXA                ;(2)
JSR $0306          ;(6)
LDY #$01           ;(2)
LDA ($00),Y        ;(5/6)
CMP #$02           ;(2)
BNE $007B          ;(2/3)
.BYTE $B2          ;%10110010
BRK                ;(7)
CMP #$58           ;(2)
BCS $00B5          ;(2/3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
LDX #$00           ;(2)
TYA                ;(2)
JSR $029A          ;(6)
.BYTE $80          ;%10000000
TSX                ;(2)
JMP $02F8          ;(3)
LDY #$00           ;(2)
CLC                ;(2)
ADC ($00),Y        ;(5/6)
STA ($00),Y        ;(6)
PHA                ;(3)
INY                ;(2)
TXA                ;(2)
ADC ($00),Y        ;(5/6)
STA ($00),Y        ;(6)
TAX                ;(2)
PLA                ;(4)
RTS                ;(6)
LDY #$00           ;(2)
BEQ $00D4          ;(2/3)
LDA #$34           ;(2)
LDX #$03           ;(2)
JMP $0336          ;(3)
RTS                ;(6)
LDA #$34           ;(2)
STA $08            ;(3)
LDA #$03           ;(2)
STA $09            ;(3)
LDA #$34           ;(2)
STA $0A            ;(3)
LDA #$03           ;(2)
STA $0B            ;(3)
LDX #$D8           ;(2)
LDA #$FF           ;(2)
STA $10            ;(3)
LDY #$00           ;(2)
INX                ;(2)
BEQ $00FD          ;(2/3)
LDA ($08),Y        ;(5/6)
STA ($0A),Y        ;(6)
INY                ;(2)
BNE $00ED          ;(2/3)
INC $09            ;(5)
INC $0B            ;(5)
BNE $00ED          ;(2/3)
INC $10            ;(5)
BNE $00F0          ;(2/3)
RTS                ;(6)
LDA $00            ;(3)
SEC                ;(2)
SBC #$02           ;(2)
STA $00            ;(3)
BCC $010C          ;(2/3)
RTS                ;(6)
DEC $01            ;(5)
RTS                ;(6)
LDY #$01           ;(2)
LDA ($00),Y        ;(5/6)
TAX                ;(2)
.BYTE $B2          ;%10110010
BRK                ;(7)
INC $00            ;(5)
BEQ $011F          ;(2/3)
INC $00            ;(5)
BEQ $0121          ;(2/3)
RTS                ;(6)
INC $00            ;(5)
INC $01            ;(5)
RTS                ;(6)
LDY #$00           ;(2)
STA ($00),Y        ;(6)
INY                ;(2)
PHA                ;(3)
TXA                ;(2)
STA ($00),Y        ;(6)
PLA                ;(4)
RTS                ;(6)
LDA #$5B           ;(2)
STA $08            ;(3)
LDA #$03           ;(2)
STA $09            ;(3)
LDA #$00           ;(2)
TAY                ;(2)
LDX #$00           ;(2)
BEQ $0148          ;(2/3)
STA ($08),Y        ;(6)
INY                ;(2)
BNE $013E          ;(2/3)
INC $09            ;(5)
DEX                ;(2)
BNE $013E          ;(2/3)
CPY #$00           ;(2)
BEQ $0151          ;(2/3)
STA ($08),Y        ;(6)
INY                ;(2)
BNE $0148          ;(2/3)
RTS                ;(6)
BRK                ;(7)
BRK                ;(7)
STA $0344          ;(4)
STX $0345          ;(4)
STA $034B          ;(4)
.END

;auto-generated symbols and labels
 L0036        $36
 L005D        $5D
 L0097        $97
 L00B6        $B6
 L00DF        $DF
 L00CF        $CF
 L00D2        $D2
 L00EE        $EE
 L0101      $0101
 L0103      $0103
 L012A      $012A
 L0120      $0120
 L0133      $0133
 L0142      $0142

Cycle-count is the comment per command, mind you. This assembly isn't the smallest/least wasteful because it still defines a static variable, but said variable may be re-used often, so I decided to just use what the bitmapgraphics library does but do the calculations myself to avoid wasteful math (I'd assume the compiler would do it for us, but no reason not to hard-code when we're assuming the 8bpp/ 256 color mode at 320x180 for starters)

Now, we can go even more basic than that though. A function that just writes one 0x0000 value to the RIA XRAM is:

* = $0000
LDX #$FF           ;(2)
TXS                ;(2)
CLD                ;(2)
LDA #$00           ;(2)
STA $00            ;(3)
LDA #$F7           ;(2)
STA $01            ;(3)
JSR $027F          ;(6)
JSR $0252          ;(6)
JSR $022B          ;(6)
JSR $0237          ;(6)
.BYTE $DA          ;%11011010
PHA                ;(3)
JSR $0246          ;(6)
PLA                ;(4)
STA $FFF4          ;(4)
.BYTE $FA          ;%11111010
STX $FFF6          ;(4)
LDA #$FF           ;(2)
STA $FFEF          ;(4)
.BYTE $DB          ;%11011011
LDY #$00           ;(2)
BEQ $0036          ;(2/3)
LDA #$37           ;(2)
LDX #$02           ;(2)
JMP $02A2          ;(3)
RTS                ;(6)
.BYTE $9C          ;%10011100
INC $FF            ;(5)
.BYTE $9C          ;%10011100
.BYTE $E7          ;%11100111
.BYTE $FF          ;%11111111
LDA #$01           ;(2)
STA $FFE5          ;(4)
.BYTE $9C          ;%10011100
CPX $FF            ;(3)
RTS                ;(6)
LDY #$00           ;(2)
BEQ $0051          ;(2/3)
LDA #$A2           ;(2)
LDX #$02           ;(2)
JMP $02A2          ;(3)
RTS                ;(6)
LDA #$A2           ;(2)
STA $08            ;(3)
LDA #$02           ;(2)
STA $09            ;(3)
LDA #$A2           ;(2)
STA $0A            ;(3)
LDA #$02           ;(2)
STA $0B            ;(3)
LDX #$DA           ;(2)
LDA #$FF           ;(2)
STA $10            ;(3)
LDY #$00           ;(2)
INX                ;(2)
BEQ $007A          ;(2/3)
LDA ($08),Y        ;(5/6)
STA ($0A),Y        ;(6)
INY                ;(2)
BNE $006A          ;(2/3)
INC $09            ;(5)
INC $0B            ;(5)
BNE $006A          ;(2/3)
INC $10            ;(5)
BNE $006D          ;(2/3)
RTS                ;(6)
LDA #$C7           ;(2)
STA $08            ;(3)
LDA #$02           ;(2)
STA $09            ;(3)
LDA #$00           ;(2)
TAY                ;(2)
LDX #$00           ;(2)
BEQ $0098          ;(2/3)
STA ($08),Y        ;(6)
INY                ;(2)
BNE $008E          ;(2/3)
INC $09            ;(5)
DEX                ;(2)
BNE $008E          ;(2/3)
CPY #$00           ;(2)
BEQ $00A1          ;(2/3)
STA ($08),Y        ;(6)
INY                ;(2)
BNE $0098          ;(2/3)
RTS                ;(6)
STA $02B0          ;(4)
STX $02B1          ;(4)
STA $02B7          ;(4)
STX $02B8          ;(4)
DEY                ;(2)
LDA $FFFF,Y        ;(4/5)
STA $02C1          ;(4)
DEY                ;(2)
LDA $FFFF,Y        ;(4/5)
STA $02C0          ;(4)
STY $02C3          ;(4)
JSR $FFFF          ;(6)
LDY #$FF           ;(2)
BNE $00AE          ;(2/3)
RTS                ;(6)
.END

;auto-generated symbols and labels
 L0036        $36
 L0051        $51
 L007A        $7A
 L006A        $6A
 L006D        $6D
 L0098        $98
 L008E        $8E
 L00A1        $A1
 L00AE        $AE

Sadly, I can't exactly comprehend much more currently, IE how to best translate the symbols/addresses until their corresponding actual variables/names/calls etc.

rumbledethumps commented 11 months ago

Try looking at the assembly generated by CC65. It's in the build folder.

Hyenadae commented 11 months ago

Ah, so that's what's in the CMake subfolders. Lots of files to distract me. Okay, that makes things a lot easier for seeing the individual functions, but don't have the symbol/label addr map

/rp6502-vscode-gfx/build/CMakeFiles/erasexram.dir/src/(.s)

Now to read up on how to set a variable to ZeroPage properly / more CC65 documentation:

It compiles, but it'll work if the (sp) is in zero page (from the .s?) It seems to write to ZP in the fully disassembled ROM though?

void main()
{

    #pragma bss-name (push,"ZEROPAGE")
    #pragma data-name(push,"ZEROPAGE")
    uint8_t color;
    uint16_t x; 
    uint8_t y;
    #pragma bss-name (pop)
    #pragma data-name(pop)

        color = 255; // temp val
        x = 319; // temp val
        y = 179; // temp val

        RIA.addr0 = 320 * y + x;
        RIA.step0 = 1;
        RIA.rw0 = color;
}

Gives us:

000000r 1               ; ---------------------------------------------------------------
000000r 1               ; void __near__ main (void)
000000r 1               ; ---------------------------------------------------------------
000000r 1               
000000r 1               .segment    "CODE"
000000r 1               
000000r 1               .proc   _main: near
000000r 1               
000000r 1               .segment    "CODE"
000000r 1               
000000r 1               ;
000000r 1               ; color = 255;
000000r 1               ;
000000r 1  20 rr rr         jsr     decsp4
000003r 1  A9 FF            lda     #$FF
000005r 1  A0 03            ldy     #$03
000007r 1  91 rr            sta     (sp),y
000009r 1               ;
000009r 1               ; x = 319;
000009r 1               ;
000009r 1  A2 01            ldx     #$01
00000Br 1  A9 3F            lda     #$3F
00000Dr 1  A0 01            ldy     #$01
00000Fr 1  20 rr rr         jsr     staxysp
000012r 1               ;
000012r 1               ; y = 179;
000012r 1               ;
000012r 1  A9 B3            lda     #$B3
000014r 1  92 rr            sta     (sp)
000016r 1               ;
000016r 1               ; RIA.addr0 = 320 * y + x;
000016r 1               ;
000016r 1  B2 rr            lda     (sp)
000018r 1  20 rr rr         jsr     pusha0
00001Br 1  A2 01            ldx     #$01
00001Dr 1  A9 40            lda     #$40
00001Fr 1  20 rr rr         jsr     tosmulax
000022r 1  85 rr            sta     ptr1
000024r 1  86 rr            stx     ptr1+1
000026r 1  A0 02            ldy     #$02
000028r 1  20 rr rr         jsr     ldaxysp
00002Br 1  18               clc
00002Cr 1  65 rr            adc     ptr1
00002Er 1  8D E6 FF         sta     $FFE6
000031r 1  8A               txa
000032r 1  65 rr            adc     ptr1+1
000034r 1  8D E7 FF         sta     $FFE6+1
000037r 1               ;
000037r 1               ; RIA.step0 = 1;
000037r 1               ;
000037r 1  A9 01            lda     #$01
000039r 1  8D E5 FF         sta     $FFE5
00003Cr 1               ;
00003Cr 1               ; RIA.rw0 = color;
00003Cr 1               ;
00003Cr 1  A0 03            ldy     #$03
00003Er 1  B1 rr            lda     (sp),y
000040r 1  8D E4 FF         sta     $FFE4
000043r 1               ;
000043r 1               ; }
000043r 1               ;
000043r 1  4C rr rr         jmp     incsp4
000046r 1               
000046r 1               .segment    "DATA"
000000r 1               
000000r 1               .segment    "ZEROPAGE"
000000r 1               .segment    "DATA"
000000r 1               
000000r 1               .endproc
000000r 1               
000000r 1     

Or,

          * = $0000
          LDX #$FF
          TXS
          CLD
          LDA #$00
          STA $00
          LDA #$F7
          STA $01
          JSR $03A6
          JSR $0297
          JSR $022B
          JSR $0237
          .BYTE $DA    ;%11011010
          PHA
          JSR $028B
          PLA
          STA $FFF4
          .BYTE $FA    ;%11111010
          STX $FFF6
          LDA #$FF
          STA $FFEF
          .BYTE $DB    ;%11011011
          LDY #$00
          BEQ L0036
          LDA #$37
          LDX #$02
          JMP $03C9
L0036     RTS
          JSR $02C4
          LDA #$FF
          LDY #$03
          STA ($00),Y
          LDX #$01
          LDA #$3F
          LDY #$01
          JSR $039D
          LDA #$B3
          .BYTE $92    ;%10010010
          BRK
          .BYTE $B2    ;%10110010
          BRK
          JSR $0383
          LDX #$01
          LDA #$40
          JSR $02F4
          STA $08
          STX $09
          LDY #$02
          JSR $02ED
          CLC
          ADC $08
          STA $FFE6
          TXA
          ADC $09
          STA $FFE7
          LDA #$01
          STA $FFE5
          LDY #$03
          LDA ($00),Y
          STA $FFE4
          JMP $02E6
          INY
          PHA
          CLC
          TYA
          ADC $00
          STA $00
          BCC L0089
          INC $01
L0089     PLA
          RTS
          LDY #$00
          BEQ L0096
          LDA #$C9
          LDX #$03
          JMP $03C9
L0096     RTS
          LDA #$C9
          STA $08
          LDA #$03
          STA $09
          LDA #$C9
          STA $0A
          LDA #$03
          STA $0B
          LDX #$DA
          LDA #$FF
          STA $10
          LDY #$00
L00AF     INX
          BEQ L00BF
L00B2     LDA ($08),Y
          STA ($0A),Y
          INY
          BNE L00AF
          INC $09
          INC $0B
          BNE L00AF
L00BF     INC $10
          BNE L00B2
          RTS
          LDA $00
          SEC
          SBC #$04
          STA $00
          BCC L00CE
          RTS
L00CE     DEC $01
          RTS
          LDY #$01
          LDA ($00),Y
          TAX
          .BYTE $B2    ;%10110010
          BRK
          INC $00
          BEQ L00E1
          INC $00
          BEQ L00E3
          RTS
L00E1     INC $00
L00E3     INC $01
          RTS
          LDY #$04
          JMP $027E
          LDY #$01
          LDA ($00),Y
          TAX
          DEY
          LDA ($00),Y
          RTS
          STA $0E
          TXA
          BEQ L0127
          STX $0F
          JSR $0373
          TYA
          LDY $09
          BEQ L012A
          STA $10
          LDY #$10
          LSR $0F
          ROR $0E
L010B     BCC L0118
          CLC
          ADC $08
          TAX
          LDA $09
          ADC $10
          STA $10
          TXA
L0118     ROR $10
          ROR A
          ROR $0F
          ROR $0E
          DEY
          BNE L010B
          LDA $0E
          LDX $0F
          RTS
L0127     JMP $033B
L012A     STX $09
          LDY $08
          LDX $0E
          STX $08
          STY $0E
          LDY #$08
          JMP $0345
          STA $0E
          JSR $0373
          TYA
          LDY #$08
          LDX $09
          BEQ $0162
          STA $0F
          LSR $0E
          BCC L0156
          CLC
          ADC $08
          TAX
          LDA $09
          ADC $0F
          STA $0F
          TXA
L0156     ROR $0F
          ROR A
          ROR $0E
          .END

;auto-generated symbols and labels
 L0036        $36
 L0089        $89
 L0096        $96
 L00BF        $BF
 L00AF        $AF
 L00B2        $B2
 L00CE        $CE
 L00E1        $E1
 L00E3        $E3
 L0127      $0127
 L012A      $012A
 L0118      $0118
 L010B      $010B
 L0156      $0156

But, it seems that there are a lot of stack/offset based operations still happening from what should be pretty simple things (ie, writing 255 179 and 319 to Color, X, Y in ZP should be 'simple' for init values)

I guess in BASIC/assembly the new test variable init functions and definitions would be like (excluding stack handling operations and writing/restoring prior registers etc):

zp.inc 
COLOR = $EE ;(8bit)
HGRY = $ED ;(8bit)
HGRX_low= $EB
HGRX_high= X_low+1

gfx.s
LAB_WCOLOR:
LDA #$FF
STA COLOR

LAB_SCRYPOS:
LDA #$B3  ;;179
STA HGRY

LAB_SCRXPOS:
LDA #$3F
LDY #$01
STY HGRX_low
STA HGRX_high
linuxplayground commented 11 months ago

When the compiler creates assembly it uses a psuedo stack for passing arguments back and forth unless the argument can be handled in the accumulator or the A and X registers to hold a word of data. Usually a pointer. This is because C is a generalized language and the compiler has to make do. Global variables can really speed things up.

So when looking at the assembly created by the compiler consider how you can simplify it for a specific use case and avoid all the psuedo stack manipulation.

For example: rather than pushing arguments into a stack that needs its own pointer in zeropage and then later popping them off to consume them all the while managing that stack pointer, you can maybe use the hardware stack. Or just reserve some BSS variable memory and reference the data as globals.

While not necessarily safe in a purist programming model, it's going to be much faster. And with careful programming you can manage it quite well.

I typically reserve variable memory for different subsystems in pages 02 through 04 and I treat them as globals.

I also hardly ever write 6502 code in C... Maybe I should. But every byte counts, every cycle counts.... It hurts my brain to read c generated assembly. 😂

netzerohero commented 10 months ago

At long last, attached below find initial graphic-plotting support source files, along with three test/demo Basic programs. Targets the current (20-Dec-2023) EhBASIC in the repo (rev: 63ca0e6).

ehbasic_20231228_intial_plot_works.zip

Four new commands are available using EhBasic's "CALL" keyword. CALL addresses supplied are from the mapfile. Plotting command callable from EhBASIC.

The commands are:

Assumptions / Limitations:

  1. HPLOT targets (only) the 320h x 180v x 8-bit-color mode of the rp6502's pico-VGA.
  2. The x-coordinate from EhBasic is limited to values >= 255 (8-bits). This is a limitation of parameters following the 'CALL' EhBASIC keyword.

My challenges were understanding:

  1. The pico-VGA graphics modes / canvas model. Its versatile HW and capabilities remain a mystery. I narrowly focused on a very limited mode-of-operation (bitmap graphic and console/text mode) to quickly add some capability to the exiting EhBASIC and proof-out concepts.
  2. The integration of EhBASIC's comma-separated parameters following the 'CALL' command to the new functions coded-in-C.
  3. C-stack, 'fast-call' and the assembly / C environment and tooling. Enough is running now to share IMHO.

The additional source files include:

The Basic demo/test code includes:

20231228_153949

EhBasic source mods were limited to:

All other EhBasic source remains untouched.
I purposefully did not add the four new commands to EhBasic's command token/system. That additional complexity was not currently needed to demonstrate the concepts. And doing so undoubtedly opens-up additional conversations on syntax, etc.

Hopefully this effort will prove helpful, at least in providing example concepts. And perhaps in bringing heritage basic graphics-code to the rp6502. I'm happy to have reached this point; it was challenging. Refinements and improvements are admittedly necessary. Insights and comments welcomed.

Happy Holidays!

Hyenadae commented 10 months ago

Oh wow, thanks a lot for sorting this out! I wish I was able to help more or be caught up earlier, but I had a pretty bad issue with my foot (all my muscles were cramped/really pulled up) somehow after a sleep and hurt a lot for over a week. Managed to get better and walking just before christmas day, but yeah, that's a lot of my vacation used up on some random problem.

One thing I'm trying to figure out is how you found out the address to put HPLOT for the

20 LET HGR = $E8B2 : REM F_HGR->init_bitmap_graphics() 30 LET HPLOT = $E8B6 : REM F_HPLOT->draw_pixel() 40 LET TEXTMODE = $E8F2 : REM F_TEXT->init_console_text() 50 LET HOME = $E8F6 : REM F_CLS->cls() 55 LET CLS = HOME : REM cls()

code.

Did the cc65 compiler kit tell you this with the additional debug data that Rumble mentioned a few weeks ago? Will you be making a proper commit with all the changes after the holidays as well, after any cleanup/optimization is done if you feel it's needed. Now I figure with what you've learned (and the other more capable coders here) writing the new I/O functions will be easier, though officially adding the new functions to the tokens list to make BASIC porting even more seamless will be a project thing as you said

I'm excited to try out the other Rosettacode examples I wanted to try. Maybe we can get enough functions working for a Game Of Life port from the Applesoft BASIC :D

Next aliases for compatibility with other basics:

For MSX Basic: SCREEN 1 (and 2, sorta) [HiRes Graphics. just re-use HGR] PSET (X,Y) [Reuse HPLOT/HDRAW. A pixel with no adjustable color parms, just 'white' or green if we want to go retro] LOCATE (X,Y) [Reuse HPLOT. Pixel written is X+1 in reality, because it sets the cursor to X, but write is pushed 'forwards' by one]

https://www.atarimagazines.com/creative/v10n2/204_Simple_screen_graphics_wi.php

One issue is some examples in GW-Basic assumes you can print text on the bitmap screen. IE, LOCATE to a certain cursor position before printing in normal fonts. I figure we can't do that as we don't have a bitmap font draw function yet once we're out of terminal mode. Oh well :)

Another Applesoft BASIC command that should be stubbed is HCOLOR, as an override to HPLOT's third argument for color. Not very important, but I'll need to strip that command out of all examples for testing/showing off examples

Hyenadae commented 10 months ago

Another fun thing, I started porting over the Archimedean Spiral but I've got an issue with Lines 220/230 giving "Array bounds error" with the COS and SIN functions

It always stops after R=100.001, so I did check out another EHBasic script that uses SIN and COS,

100 REM SIN and COS wave draw for EhBASIC
110 REM (c) L.Davison 2003/4/5
120 REM leeedavison@lycos.co.uk

130 REM scale :  offset  : curve centre
140 SC = 18.5 : OF = 1.5 : CS = SC + OF
150 WIDTH 64 : REM makes POS() absolute

160 DO
170 FOR A = 0 TO TWOPI STEP PI / 10
180 S = INT(SIN(A)*SC)
190 C = INT(COS(A)*SC)
200 IF S<C THEN PRINT SPC(CS+S)"+";SPC(C-S)"x";
210 IF S>C THEN PRINT SPC(CS+C)"x";SPC(S-C)"+";
220 PRINT SPC(CS+CS-POS(0));" ."
230 FOR D = 1 TO 400 : NEXT
240 NEXT
250 LOOP

http://www.6502.org/users/mycorner/68k/ehbasic/sincos.html

It seems like for any variable that stores the output for SIN or COS, you need? to force it into an INT. So after making the lines to this:

 10 REM Archimedean Spiral
 15 REM FROM: ROSETTA CODE WEBSITE 
 20 REM Conversion from Applesoft BASIC to EHBASIC on RP6502
 25 REM Applesoft screen is 280h x 192v
 30 REM Our early rev rpg6502 EhBASIC screen is 320h x 180v 

 50 REM Addresses for our four new functions from mapfile:
 55 LET      HGR = $E8B2  : REM F_HGR->init_bitmap_graphics()
 60 LET    HPLOT = $E8B6  : REM F_HPLOT->draw_pixel()
 65 LET TEXTMODE = $E8F2  : REM F_TEXT->init_console_text()
 70 LET     HOME = $E8F6  : REM F_CLS->cls()
 75 LET      CLS = HOME   : REM cls()
 82 LET    GREEN = 10     : REM Green ON EHBASIC HPLOT 

110 LET H = 90
115 REM LET H = 96 : REM For Applesoft
120 LET W = 135
125 REM LET W = H + H/2 : REM For Applesoft
130 CALL HGR
135 REM HGR2 : REM For Applesoft
140 REM HCOLOR= 3 : REM For Applesoft
150 LET A = 1
160 LET B = 3
165 REM LET B=9 : REM For Applesoft
170 REM LET PI = 3.1415926535 : REM For Applesoft
180 LET M = 10 * PI
190 LET S = .02
200 FOR T = S TO M STEP S
210     LET R = A + B * T
220     LET Y = INT(R * SIN(T) +H)
225 REM Add INT( ) to operations with mult/add with SIN(Var)
230     LET X = INT(R * COS(T) +W)
240     IF X < 0 THEN  290
250     IF Y < 0 THEN  290 
260     IF X > 319 THEN  290
265 REM IF X > 279 THEN 290 : REM For Applesoft
270     IF Y > 179 THEN 290
275 REM IF Y > 191 THEN 290 : REM For Applesoft
280     CALL HPLOT,X,Y,GREEN
285 REM HPLOT X,Y : REM For Applesoft (White pixels)
290 NEXT

I get a very nice, almost perfect though a little squished spiral :) I think I documented most of the changes image

netzerohero commented 10 months ago

Oh wow, thanks a lot for sorting this out! I'm excited to try out the other Rosettacode examples I wanted to try.

Glad you've found those mods useful; you're the 1st to comment.

One thing I'm trying to figure out is how you found out the address to put HPLOT...

Look in file "basic.map" in your build directory. You'll see the exported addresses (1st two lines):

Exports list by name:
---------------------
F_CLS                     00E8F6 RLA    F_HGR                     00E8B2 RLA    
F_HPLOT                   00E8B6 RLA    F_TEXT                    00E8F2 RLA    
IrqBase                   0000DD REZ    LAB_COLD                  00C04D RLA    
LAB_FCER                  00CF9E RLA    LAB_SCGB                  00D552 RLA    
LAB_WARM                  000000 REZ    NmiBase                   0000DC REZ    
V_INPT                    00E89B RLA    V_LOAD                    00E8A6 RLA    
V_OUTP                    00E892 RLA    V_SAVE                    00E8A6 RLA    
V_USR                     00E8A7 RLA    __BSS_RUN__               00EF79 RLA  

Will you be making a proper commit with all the changes...

I made a public-repo forked from @rumbledethumps main EhBASIC repo; then added the enhanced code. I'm new to Github, git and pull-requests - but I eventually figured-out from @linuxplayground's recent EhBasic mixed case mod that that would be the path / possible work flow.

As far as:

a proper commit

...that's not for me to say. I'd prefer folks take a look at the code; review it - mostly for its layout / modularization and hooks w/ EhBASIC source along with the C-environment co-existence. And consider any possible incoming upstream EhBasic maintenance - which could break any enhancements - before "charging forward" (like adding commands to the "tokens list" - which will eat into the memory-footprint). Using BASIC's "CALL" allows for a level of independence from some of those concerns. Like I mentioned earlier: crawl, walk, run. Still - the present state for me still is "crawling, about-to-walk". :)

One issue is some examples in [some BASICs] assumes you can print text on the bitmap screen.

Sort of related: a x,y addressable text console would be a nice feature (i.e., VTAB(y), HTAB(x) or ansi screen addressing); porting some historical BASIC code would need such a feature.

All good ideas I suspect; but need prioritization of features / issues / RFCs - especially on an SBC that has 3 active target CPUs! I'd suggest at least first capturing as (a) an RFC in discussions. Then if there is considerable interest in the RFC capture (b) "an issue" as I did for EhBASIC - now there currently are three: Plotting (where this thread lives!), Save/Load and Xmem-peek/poke.

rumbledethumps commented 10 months ago

Now that I've staked a claim to memory for save and load, I was thinking about leaving $F000-$FEFF unused for "CALL projects" like this. Is ~4KB enough to be useful for graphics? I don't want to burn another 4K unless absolutely necessary.

Keep in mind there can be different 4KB modules that support different resolutions and whatnot. We don't need one massive library that does everything. The everything library could come later with keywords and other niceties.

netzerohero commented 10 months ago

FWIW - ~4KBytes should be OK. Here is my quick analysis; see if you agree with the reasoning:

On the "minimalist" side: My current bare-bones graphic-plotting (supporting 2-screen dimension-modes, point-plotting-only (no lines)) takes ~900-bytes.

On a full-feature "everything library" side: Tonyvr/vruumllc's full-feature graphic-library (supporting multiple screen dimensions, points, lines, text-graphics, geometric-figures, and fills) takes ~7.4KBytes.

Both are mostly coded in C (I have some minimum assembly 'glue' in rp6502enhance.s). So 4KB is between those two extremes, and should be OK.

From the map-files:

Netzerohero's "bare-bones" point-plotting enhancements (2 screen-dimension support of 10-Jan-2024):
rp6502enhance.s.obj:
    CODE              Offs=002865  Size=00004C  Align=00001  Fill=0000
basgraf.c.obj:
    CODE              Offs=0028B1  Size=00032D  Align=00001  Fill=0000
    DATA              Offs=000000  Size=000006  Align=00001  Fill=0000
----------------------------------------------------------------------
                            Total-Size = 0x37F = 895-decimal

Tonyvr/vruumllc's full-feature graphics library (~7-Nov-2023):
bitmap_graphics.c.obj:
    CODE              Offs=000000  Size=001756  Align=00001  Fill=0000
    RODATA            Offs=000000  Size=000619  Align=00001  Fill=0000
    DATA              Offs=000000  Size=00001A  Align=00001  Fill=0000
----------------------------------------------------------------------
                           Total-Size = 0x1D89 = 7561-decimal

Hope this helps.

rumbledethumps commented 10 months ago

I tried a few approaches to making modules but nothing stood out as better than what we have. I suppose it really wouldn't be awful to maintain multiple BASICs. One close to how Lee designed it that maximizes memory, matches existing documentation, and is easy to fork. And another with new keywords and a big library for the Picocomputer. Let's try that for a while.

The next step for graphics is to plan out the keywords and library. Assume the xreg/xpoke/etc keywords will be in there too. We can call this "enhanced basic plus" until something better comes along. I made an official fork and sent out invitations.

On basic not-plus I changed the starting address back to $D000. This gives us a "finished and completed" BASIC. 52735 Bytes free. 1195 bytes unused. Maybe we call this "EhBASIC 12k"?

I'll keep BASIC-12K in good working order but mostly I'm stepping back from the graphics stuff so I can play in the C realm.

netzerohero commented 10 months ago

Excellent - thank you for setting-up this guiding 2-repo structure and plan on EhBASIC; helpful - especially in grappling with git/GitHub workflow methods.

Understood - good approach moving forward: 1) Basic-EhBASIC-12K (stable w/ pico-save/load, $D000-org/max-user-memory) 2) EBP - EhBASIC-plus (experimental) - new features tryout playground (e.g., graphics, & other possible extensions)

Snapshot w/ experimental plotting working; binaries here.

2a: next-steps: (this issue and potiential-new-issues)

The next step for graphics is to plan out the keywords and library. Assume the xreg/xpoke/etc keywords will be in there

netzerohero commented 10 months ago

Closing issue.
Initial graphics support provided on: repo: ehbasic-plus tag/release: v00.00.20240114b commit: b9b0991

Tested manually OK with version-0.6 of the firmware.