Closed netzerohero closed 10 months ago
Graphics within EhBASIC can be incrementally added over time:
Not sure if you've had any time to think or plan this out further, but what do you think about:
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)
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
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
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.
Try looking at the assembly generated by CC65. It's in the build folder.
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
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. 😂
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:
My challenges were understanding:
The additional source files include:
The Basic demo/test code includes:
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!
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
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
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.
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.
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.
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.
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
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.
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.