Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Blacklord

#1
RBBS-128 1.0 is the first functional proof release containing:

  • TCPSRV-based incoming call handling and disconnect detection
  • C128 SwiftLink/ACIA serial I/O
  • user registration and password login
  • persistent REL user records
  • caller counters/history
  • SYSOP flag/privileges
  • multiple message areas
  • dedicated Private Mail area
  • public/private message entry
  • message retrieval
  • summary scanning
  • new-message reading
  • per-user/per-area last-read tracking
  • replies
  • message deletion with ownership/SYSOP rules
  • Australian date handling

The current command loop already represents the core messaging release nicely:

a - change message area
e - enter a message
p - reply to a message
r - retrieve a message
s - scan message summaries
n - read new messages
k - kill a message
g - goodbye
? - help
#2
Interim code (15/8/2026) (WIP)

10 rem rbbs-128 1.0 main program
20 da=56832:sa=56833:ca=56834:ta=56835
30 sd=8:dd=9:fd=10
40 fast
45 dim m(200,2),ms$(10),ya$(16),ys$(16),ym$(16),yt$(16)
50 graphic 5
60 poke ta,30:poke ca,11
62 c1$=chr$(35)+chr$(67)+chr$(79)+chr$(78)+chr$(78)+chr$(69)+chr$(67)+chr$(84)+chr$(58)
64 c3$=chr$(35)+chr$(68)+chr$(73)+chr$(83)+chr$(67)+chr$(79)+chr$(78)+chr$(78)+chr$(69)+chr$(67)+chr$(84)
70 scnclr
80 print "rbbs-128 1.0"
90 print
100 print "commodore 128 remote bulletin board v1.0"
110 print
120 gosub 8300:gosub 10200:goto 7000

1000 rem caller login
1010 ur=0:hm=0:mf$="":pw$="":ci$=""
1020 a1$="enter your first name: ":n=1:gosub 5040
1030 c=1:ml=20:gosub 5170
1040 if cd then goto 7900
1050 n$=b$
1060 if n$="" then 1020
1070 if n$<"a" or len(n$)=1 then 1020
1080 ck=0
1090 for ix=1 to len(n$)
1100 if mid$(n$,ix,1)=" " then ck=1
1110 next ix
1120 if ck=0 then 1150
1130 a$="please do not use spaces in your name":gosub 5040
1140 gosub 5040:goto 1020
1150 a1$="enter your last name:  ":n=1:gosub 5040
1160 c=1:ml=20:gosub 5170
1170 if cd then goto 7900
1180 o$=b$
1190 if o$="" then 1020
1200 if o$<"a" or len(o$)=1 then 1020
1210 ck=0
1220 for ix=1 to len(o$)
1230 if mid$(o$,ix,1)=" " then ck=1
1240 next ix
1250 if ck=0 then 1280
1260 a$="please do not use spaces in your name":gosub 5040
1270 gosub 5040:goto 1150
1280 a$="checking user file...":gosub 5040
1290 gosub 2000
1300 if ok=0 then 1400
1310 a1$="enter your password: ":n=1:gosub 5040
1320 c=2:ml=20:gosub 5170
1330 if cd then goto 7900
1340 if b$=pw$ then 1370
1350 a$="incorrect password":gosub 5040
1360 goto 1310
1370 a$="login accepted":gosub 5040
1380 a$="welcome "+n$+" "+o$:gosub 5040
1390 goto 1800

1400 rem new caller
1410 a$="new user registration":gosub 5040
1420 gosub 2200
1430 if cd then goto 7900
1440 if ok=0 then 1020
1450 a$="registration complete":gosub 5040
1460 a$="welcome "+n$+" "+o$:gosub 5040

1800 rem login complete
1810 lg=-1
1820 gosub 8700
1830 gosub 5040
1840 a$="you are caller number:"+str$(tc):gosub 5040
1850 a$="rbbs-128 login complete.":gosub 5040
1860 a$="date/time: "+dd$+"/"+mo$+"/"+yr$+" "+tm$:gosub 5040
1870 gosub 9000
1880 goto 4000

2000 rem check rel users file
2010 ok=0:pw$="":ci$="":hm=0:ur=0:mf$=""
2020 open 15,dd,15
2030 open 2,dd,2,"users"
2040 rn=1:gosub 8000:gosub 8100
2050 nu=val(r$)
2060 if nu<1 then 2160
2070 for ur=2 to nu+1
2080 rn=ur:gosub 8000:gosub 8100
2090 u$=mid$(r$,3)
2100 gosub 2700
2110 if f1$=n$ and f2$=o$ then 2140
2120 next ur
2130 goto 2160
2140 mf$=left$(r$,1)
2150 pw$=f4$:ci$=f3$:hm=val(f5$):ok=-1
2160 close 2
2170 close 15
2180 return

2200 rem new user registration
2210 ok=0
2220 a1$="city/state: ":n=1:gosub 5040
2230 c=1:ml=30:gosub 5170
2240 if cd then return
2250 ci$=b$
2260 if len(ci$)<2 then 2220
2270 a1$="choose a password: ":n=1:gosub 5040
2280 c=2:ml=20:gosub 5170
2290 if cd then return
2300 p1$=b$
2310 if len(p1$)<3 then 2270
2320 a1$="enter password again: ":n=1:gosub 5040
2330 c=2:ml=20:gosub 5170
2340 if cd then return
2350 if b$=p1$ then 2390
2360 a$="passwords do not match":gosub 5040
2370 gosub 5040
2380 goto 2270
2390 gosub 5040
2400 a$="name: "+n$+" "+o$:gosub 5040
2410 a$="city/state: "+ci$:gosub 5040
2420 a1$="is this correct (y/n)? ":n=1:gosub 5040
2430 c=1:ml=1:gosub 5170
2440 if cd then return
2450 if b$="" then 2420
2460 if asc(b$)=89 then 2500
2470 if asc(b$)=78 then 2220
2480 goto 2420

2500 rem append new rel user
2510 ok=0
2520 open 15,dd,15
2530 open 2,dd,2,"users"
2540 rn=1:gosub 8000:gosub 8100
2550 nu=val(r$)
2560 rn=nu+2:ur=rn
2570 gosub 8000
2580 r$="  "+n$+";"+o$+";"+ci$+";"+p1$+";0"
2590 gosub 8200
2600 rn=1:gosub 8000
2610 r$=str$(nu+1)
2620 gosub 8200
2630 close 2
2640 input#15,en,em$,et,es
2650 close 15
2660 if en<>0 then 2690
2670 pw$=p1$:hm=0:mf$=" ":ok=-1
2680 return
2690 a$="error writing users file":gosub 5040
2695 return

2700 rem split user record
2710 f1$="":f2$="":f3$="":f4$="":f5$=""
2720 fi=1
2730 for ix=1 to len(u$)
2740 q$=mid$(u$,ix,1)
2750 if q$=";" then fi=fi+1:goto 2810
2760 if fi=1 then f1$=f1$+q$
2770 if fi=2 then f2$=f2$+q$
2780 if fi=3 then f3$=f3$+q$
2790 if fi=4 then f4$=f4$+q$
2800 if fi=5 then f5$=f5$+q$
2810 next ix
2820 return

2900 rem update current user record
2910 if ur=0 then return
2920 if mf$="" then mf$=" "
2930 open 15,dd,15
2940 open 2,dd,2,"users"
2950 rn=ur:gosub 8000
2960 r$=mf$+" "+n$+";"+o$+";"+ci$+";"+pw$+";"+str$(hm)
2970 gosub 8200
2980 close 2
2990 close 15
2995 return

3000 rem sysop functions - reserved
3010 return

4000 rem temporary command loop
4010 gosub 5040
4020 a1$=n$+"? command (a,g,?): ":n=1:gosub 5040
4030 c=1:ml=1:gosub 5170
4040 if cd then goto 7900
4050 if b$="" then 4000
4060 if b$="a" then gosub 9000:goto 4000
4070 if b$="g" then 4200
4080 if b$="?" then 4100
4090 a$="i don't understand '"+b$+"'":gosub 5040
4095 goto 4000

4100 rem temporary help
4110 a$="a - change message area":gosub 5040
4120 a$="g - goodbye":gosub 5040
4130 a$="? - this help":gosub 5040
4140 goto 4000

4200 rem temporary goodbye
4210 gosub 5040
4220 a$="thanks for calling, "+n$+".":gosub 5040
4230 a$="please call again!":gosub 5040
4240 if ur>0 then gosub 2900
4250 lg=0
4260 a$="please disconnect.":gosub 5040
4270 goto 7800

5040 rem rbbs-128 output service
5050 if a1$="" then 5080
5060 a$=a1$
5070 a1$=""
5080 if len(a$)=0 then 5140
5090 for ix=1 to len(a$)
5100 ch=asc(mid$(a$,ix,1))
5110 gosub 5800
5120 gosub 5700
5130 next ix
5135 pp$=a$
5140 if n=1 then 5160
5150 ch=13:gosub 5800:ch=10:gosub 5800:print
5160 a$="":n=0:return

5170 rem rbbs-128 line input service
5180 b$=""
5190 gosub 6210
5200 return

5600 rem initialize swiftlink acia
5610 poke ta,30
5620 poke ca,11
5630 return

5700 rem display ascii byte on c128
5710 x=ch
5720 if x>=97 and x<=122 then x=x-32
5730 print chr$(x);
5740 return

5800 rem send ascii byte to tcpsrv
5810 if (peek(sa) and 16)=0 then 5810
5820 poke da,ch
5830 return

6000 rem get serial byte
6010 if (peek(sa) and 8)=0 then 6010
6020 ch=peek(da)
6030 return

6100 rem get raw tcpsrv control line
6110 ct$=""
6120 gosub 6000
6130 if ch=10 then 6120
6140 if ch=13 then 6180
6150 if ch<32 or ch>126 then 6120
6160 ct$=ct$+chr$(ch)
6170 goto 6120
6180 return

6210 rem rbbs-128 character line editor
6220 b$=""
6230 gosub 6000
6240 if ch=10 then 6230
6250 if ch=13 then 6520
6260 if ch=8 then 6400
6270 if ch=127 then 6400
6280 if ch=18 then 6450
6290 if ch=21 then 6480
6300 if ch=24 then 6480
6310 if ch=12 then 6450
6320 if ch<32 then 6230
6330 if ch>126 then 6230
6340 if len(b$)>=ml then 6230
6350 x=ch
6360 if x>=97 and x<=122 then x=x-32
6370 b$=b$+chr$(x)
6380 if c=1 then gosub 6680
6390 goto 6230

6400 rem delete last character
6410 if len(b$)=0 then 6230
6420 b$=left$(b$,len(b$)-1)
6430 if c=2 then 6230
6440 ch=8:gosub 6680
6445 goto 6230

6450 rem ctrl-r or ctrl-l - retype line
6455 if c=2 then 6230
6460 print
6465 if len(b$)=0 then 6230
6470 for ix=1 to len(b$)
6472 ch=asc(mid$(b$,ix,1))
6474 gosub 6680
6476 next ix
6478 goto 6230

6480 rem ctrl-u or ctrl-x - erase line
6485 if len(b$)=0 then 6230
6490 if c=2 then b$="":goto 6230
6495 for ix=1 to len(b$)
6500 ch=8:gosub 6680
6505 next ix
6510 b$=""
6515 goto 6230

6520 rem finish input line
6525 if right$(b$,len(c3$))=c3$ then cd=-1:return
6530 if c=1 then ch=13:gosub 6600
6535 if c=2 then print
6540 ch=13:gosub 5800
6545 ch=10:gosub 5800
6550 return

6600 rem echo ascii byte locally
6610 if ch=13 then print:return
6620 if ch=10 then return
6630 if ch=8 then print chr$(20);:return
6640 x=ch
6650 if x>=97 and x<=122 then x=x-32
6660 print chr$(x);
6670 return

6680 rem echo ascii byte local and remote
6685 gosub 5800
6690 gosub 6600
6695 return

6700 rem display ascii string locally
6710 for ix=1 to len(a$)
6720 x=asc(mid$(a$,ix,1))
6730 if x>=97 and x<=122 then x=x-32
6740 print chr$(x);
6750 next ix
6760 print
6770 return

7000 rem waiting for call
7010 cd=0:lg=0:ur=0
7020 print
7030 print "waiting for call..."
7040 gosub 6100
7050 p=instr(ct$,c1$)
7060 if p>0 then 7200
7070 goto 7040

7200 rem process connect and host date/time
7210 dt$=mid$(ct$,p+len(c1$))
7220 if len(dt$)<19 then 7300
7230 dd$=left$(dt$,2)
7240 mo$=mid$(dt$,4,2)
7250 yr$=mid$(dt$,7,4)
7260 tm$=mid$(dt$,12,8)
7270 ti$=mid$(dt$,12,2)+mid$(dt$,15,2)+mid$(dt$,18,2)
7280 print
7285 print "caller connected"
7290 print "date: ";dd$;"/";mo$;"/";yr$
7292 print "time: ";tm$
7295 goto 7400

7300 rem invalid time packet
7310 print "invalid time packet"
7320 goto 7800

7400 rem start rbbs caller session
7410 cd=0
7420 scnclr
7430 a$="rbbs-128 1.0":gosub 5040
7440 gosub 5040
7450 a$="commodore 128 remote bulletin board":gosub 5040
7460 gosub 5040
7470 goto 1000

7800 rem wait for caller disconnect
7810 gosub 6100
7820 if instr(ct$,c3$)>0 then 7900
7830 goto 7810

7900 rem caller disconnected cleanup
7910 if lg and ur>0 then gosub 2900
7920 cd=0:lg=0:ur=0
7930 n$="":o$="":pw$="":ci$="":mf$=""
7940 scnclr
7950 print "rbbs-128 1.0"
7960 print
7970 print "caller disconnected"
7980 goto 7000

8000 rem position 62 byte user record
8010 record#2,(rn),1
8020 return

8100 rem read 62 byte rel record
8110 r$=""
8120 for ri=1 to 62
8130 get#2,q$
8140 if q$="" then q$=chr$(0)
8150 r$=r$+q$
8160 next ri
8170 return

8200 rem write 62 byte rel record
8210 if len(r$)>=62 then 8240
8220 r$=r$+" "
8230 goto 8210
8240 if len(r$)>62 then r$=left$(r$,62)
8250 print#2,r$;
8260 return

8300 rem read system counters
8310 tc=0:mg=0:ls=0:rc=0
8320 dopen#4,"counters",d0,u9
8330 record#4,1,1
8340 r$=""
8350 for ri=1 to 32
8360 get#4,q$
8370 if q$="" then q$=" "
8380 r$=r$+q$
8390 next ri
8400 dclose#4
8410 z1$="":z2$="":z3$="":z4$=""
8420 zi=1
8430 for ix=1 to len(r$)
8440 q$=mid$(r$,ix,1)
8450 if q$=";" then zi=zi+1:goto 8500
8460 if zi=1 then z1$=z1$+q$
8470 if zi=2 then z2$=z2$+q$
8480 if zi=3 then z3$=z3$+q$
8490 if zi=4 then z4$=z4$+q$
8500 next ix
8510 tc=val(z1$):mg=val(z2$):ls=val(z3$):rc=val(z4$)
8520 return

8600 rem save system counters
8610 r$=str$(tc)+";"+str$(mg)+";"+str$(ls)+";"+str$(rc)
8620 if len(r$)>=32 then 8650
8630 r$=r$+" "
8640 goto 8620
8650 if len(r$)>32 then r$=left$(r$,32)
8660 dopen#4,"counters",d0,u9
8670 record#4,1,1
8680 print#4,r$;
8690 dclose#4
8695 return

8700 rem record successful caller
8710 tc=tc+1
8720 rc=rc+1
8730 r$=dd$+"/"+mo$+"/"+yr$+" "+tm$+";"+n$+";"+o$+";"+ci$
8740 if len(r$)>=96 then 8770
8750 r$=r$+" "
8760 goto 8740
8770 if len(r$)>96 then r$=left$(r$,96)
8780 dopen#5,"callers",d0,u9
8790 record#5,rc+1,1
8800 print#5,r$;
8810 record#5,1,1
8820 r$=str$(rc)
8830 if len(r$)>=96 then 8860
8840 r$=r$+" "
8850 goto 8830
8860 if len(r$)>96 then r$=left$(r$,96)
8870 print#5,r$;
8880 dclose#5
8890 gosub 8600
8895 return

9000 rem message area selector
9010 gosub 5040
9020 a$="message areas":gosub 5040
9030 gosub 5040
9040 if na<1 then 9290
9050 for ai=1 to na
9060 a$=str$(ai)+" - "+ya$(ai):gosub 5040
9070 next ai
9080 gosub 5040
9090 a1$="select area (1-"+mid$(str$(na),2)+"): ":n=1:gosub 5040
9092 c=1:ml=3:gosub 5170
9094 if cd then return
9096 if b$="" then 9090
9098 ma=val(b$)
9100 if ma<1 or ma>na then 9090
9110 an$=ya$(ma)
9120 sn$=ys$(ma)
9130 mn$=ym$(ma)
9140 at$=yt$(ma)
9150 gosub 9900
9160 gosub 5040
9170 a$="current area: "+an$:gosub 5040
9180 a$="messages:"+str$(mc):gosub 5040
9190 return
9290 a$="no message areas configured.":gosub 5040
9295 return

9600 rem read 64 byte area record
9610 r$=""
9620 for ri=1 to 64
9630 get#6,q$
9640 if q$="" then q$=" "
9650 r$=r$+q$
9660 next ri
9670 return

9700 rem split message area definition
9710 an$="":sn$="":mn$="":at$=""
9720 fi=1
9730 for ix=1 to len(r$)
9740 q$=mid$(r$,ix,1)
9750 if q$=";" then fi=fi+1:goto 9800
9760 if fi=1 then an$=an$+q$
9770 if fi=2 then sn$=sn$+q$
9780 if fi=3 then mn$=mn$+q$
9790 if fi=4 then at$=at$+q$
9800 next ix
9810 gosub 9820
9815 return

9820 rem trim area fields
9830 if len(an$)>0 and right$(an$,1)=" " then an$=left$(an$,len(an$)-1):goto 9830
9840 if len(sn$)>0 and right$(sn$,1)=" " then sn$=left$(sn$,len(sn$)-1):goto 9840
9850 if len(mn$)>0 and right$(mn$,1)=" " then mn$=left$(mn$,len(mn$)-1):goto 9850
9860 if len(at$)>0 and right$(at$,1)=" " then at$=left$(at$,len(at$)-1):goto 9860
9870 return

9900 rem load active area summary index
9910 mz=0:mx=0:iu=0:lm=0:ac=0
9920 dopen#7,(sn$),d0,u9
9930 re=1
9940 record#7,re,1
9950 gosub 10100
9960 g=val(r$)
9970 if g>9998 then 10060
9980 if g>lm then lm=g
9990 mz=mz+1
10000 if g<>0 then ac=ac+1
10010 if mz<=200 then m(mz,1)=g
10020 if g<>0 and iu=0 then iu=g
10030 re=re+5
10040 record#7,re,1
10050 gosub 10100
10052 if mz<=200 then m(mz,2)=val(r$)
10054 mx=mx+val(r$)+6
10056 re=re+1
10058 goto 9940
10060 dclose#7
10070 mc=ac
10080 return

10100 rem read 30 byte summary record
10110 r$=""
10120 for ri=1 to 30
10130 get#7,q$
10140 if q$="" then q$=" "
10150 r$=r$+q$
10160 next ri
10170 return

10200 rem load message areas at startup
10210 na=0
10220 dopen#6,"msgareas",d0,u9
10230 record#6,1,1
10240 gosub 9600
10250 na=val(r$)
10260 if na>16 then na=16
10270 if na<1 then 10380
10280 for ai=1 to na
10290 record#6,ai+1,1
10300 gosub 9600
10310 gosub 9700
10320 ya$(ai)=an$
10330 ys$(ai)=sn$
10340 ym$(ai)=mn$
10350 yt$(ai)=at$
10360 next ai
10380 dclose#6
10390 return

10400 rem per user area last-read pointers - next stage
10410 return

11000 rem common message engine - future
11010 return

14000 rem caller log and statistics - future
14010 return

15000 rem bulletins news and help - future
15010 return

16000 rem file area system - future
16010 return

17000 rem punter file transfers - future
17010 return

18000 rem doors and external programs - future
18010 return

19000 rem sysop chat and status window - future
19010 return

20000 rem ansi terminal support - future
20010 return

21000 rem reserved expansion
21010 return
#3
Diffs to the original 1981 (v3.7) CP/M version so far :

16 message areas (the original had 1), private messages removed to their own message base rather than having one large, monolithic message base.
#4
Interim code for tcpsrv.c (compiles under Microsoft Visual Studio 2026):

/*
 * tcpsrv.c
 *
 * RBBS-128 TCP/VICE communications bridge.
 *
 * Port 23232:
 *     VICE X128 / SwiftLink connection
 *
 * Port 6400:
 *     Incoming BBS caller connection
 *
 * Data flow:
 *
 *     Caller <----> TCPSRV <----> X128
 *
 * Internal control messages sent only to X128:
 *
 *     #CONNECT
 *     #TIME:DD/MM/YYYY HH:MM:SS
 *     #DISCONNECT
 *
 * Internal control messages are deliberately sent more
 * slowly than normal caller traffic because BASIC is
 * polling the emulated 6551 ACIA one character at a time.
 *
 * Usage:
 *
 *     tcpsrv.exe
 */

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

#pragma comment(lib, "Ws2_32.lib")

#define X128_PORT       23232
#define CALLER_PORT      6400
#define BUFFER_SIZE      4096

#define INVALID_CLIENT INVALID_SOCKET


/*
 * Send an entire buffer without pacing.
 *
 * Used for normal TCP destinations such as the caller.
 */
static int send_all(SOCKET socket,
                    const char *buffer,
                    int length)
{
    int total;
    int sent;

    total = 0;

    while (total < length)
    {
        sent = send(socket,
                    buffer + total,
                    length - total,
                    0);

        if (sent == SOCKET_ERROR)
        {
            return SOCKET_ERROR;
        }

        if (sent == 0)
        {
            return 0;
        }

        total += sent;
    }

    return total;
}


/*
 * Send a zero-terminated string without pacing.
 */
static int send_text(SOCKET socket,
                     const char *text)
{
    return send_all(socket,
                    text,
                    (int)strlen(text));
}


/*
 * Send ordinary caller/console data to the C128.
 *
 * 10 ms pacing is used so a pasted line is not dumped
 * into the emulated ACIA in one instantaneous TCP burst.
 */
static int send_to_x128(SOCKET socket,
                        const char *buffer,
                        int length)
{
    int i;
    int result;

    for (i = 0; i < length; i++)
    {
        result = send(socket,
                      buffer + i,
                      1,
                      0);

        if (result == SOCKET_ERROR)
        {
            return SOCKET_ERROR;
        }

        if (result == 0)
        {
            return 0;
        }

        Sleep(50);
    }

    return length;
}


/*
 * Send internal TCPSRV control messages to the C128.
 *
 * These are deliberately much slower than caller data
 * while we prove reliable reception in BASIC.
 */
static int send_control_to_x128(SOCKET socket,
                                const char *buffer,
                                int length)
{
    int i;
    int result;

    for (i = 0; i < length; i++)
    {
        result = send(socket,
                      buffer + i,
                      1,
                      0);

        if (result == SOCKET_ERROR)
        {
            return SOCKET_ERROR;
        }

        if (result == 0)
        {
            return 0;
        }

        /*
         * Conservative test value.
         * Reduce later once the real RBBS line reader
         * has been proven reliable.
         */
        Sleep(100);
    }

    return length;
}


/*
 * Generate host local date/time.
 *
 * Australian display order:
 *
 *     DD/MM/YYYY HH:MM:SS
 */
static void make_time_message(char *buffer,
                              size_t size)
{
    SYSTEMTIME st;

    GetLocalTime(&st);

    snprintf(buffer,
             size,
             "#TIME:%02u/%02u/%04u %02u:%02u:%02u\r\n",
             (unsigned int)st.wDay,
             (unsigned int)st.wMonth,
             (unsigned int)st.wYear,
             (unsigned int)st.wHour,
             (unsigned int)st.wMinute,
             (unsigned int)st.wSecond);
}


/*
 * Create a listening TCP socket.
 *
 * Both listeners currently bind to localhost only.
 */
static SOCKET create_listener(int port)
{
    SOCKET s;
    struct sockaddr_in address;
    int reuse;
    int result;

    s = socket(AF_INET,
               SOCK_STREAM,
               IPPROTO_TCP);

    if (s == INVALID_SOCKET)
    {
        return INVALID_SOCKET;
    }

    reuse = 1;

    setsockopt(s,
               SOL_SOCKET,
               SO_REUSEADDR,
               (const char *)&reuse,
               sizeof(reuse));

    ZeroMemory(&address,
               sizeof(address));

    address.sin_family = AF_INET;

    address.sin_addr.s_addr =
        htonl(INADDR_LOOPBACK);

    address.sin_port =
        htons((unsigned short)port);

    result = bind(s,
                  (struct sockaddr *)&address,
                  sizeof(address));

    if (result == SOCKET_ERROR)
    {
        closesocket(s);
        return INVALID_SOCKET;
    }

    result = listen(s,
                    4);

    if (result == SOCKET_ERROR)
    {
        closesocket(s);
        return INVALID_SOCKET;
    }

    return s;
}


/*
 * Tell the C128 that a caller has connected.
 *
 * Also send the current Windows host date/time.
 */
static int notify_connect(SOCKET x128Socket)
{
    SYSTEMTIME st;
    char message[128];

    GetLocalTime(&st);

    snprintf(message,
             sizeof(message),
             "#CONNECT:%02u/%02u/%04u %02u:%02u:%02u\r\n",
             (unsigned int)st.wDay,
             (unsigned int)st.wMonth,
             (unsigned int)st.wYear,
             (unsigned int)st.wHour,
             (unsigned int)st.wMinute,
             (unsigned int)st.wSecond);

    if (send_control_to_x128(x128Socket,
                             message,
                             (int)strlen(message))
        == SOCKET_ERROR)
    {
        return SOCKET_ERROR;
    }

    return 0;
}

/*
 * Tell the C128 that the caller disconnected.
 */
static void notify_disconnect(SOCKET x128Socket)
{
    const char *message =
        "#DISCONNECT\r\n";

    send_control_to_x128(x128Socket,
                         message,
                         (int)strlen(message));
}


/*
 * Reject an additional caller while one is online.
 */
static void reject_busy_caller(SOCKET socket)
{
    const char *message =
        "\r\n"
        "RBBS-128 is currently in use.\r\n"
        "Please try again later.\r\n";

    send_text(socket,
              message);

    shutdown(socket,
             SD_BOTH);

    closesocket(socket);
}


int main(void)
{
    WSADATA wsaData;

    SOCKET x128Listen;
    SOCKET callerListen;

    SOCKET x128Socket;
    SOCKET callerSocket;
    SOCKET newSocket;

    fd_set readSet;

    struct timeval timeout;

    unsigned char buffer[BUFFER_SIZE];

    int received;
    int result;

    unsigned char ch;


    /*
     * Initialize Winsock.
     */
    result = WSAStartup(MAKEWORD(2, 2),
                        &wsaData);

    if (result != 0)
    {
        fprintf(stderr,
                "tcpsrv: WSAStartup failed: %d\n",
                result);

        return 1;
    }


    /*
     * Create X128 listener.
     */
    x128Listen =
        create_listener(X128_PORT);

    if (x128Listen == INVALID_SOCKET)
    {
        fprintf(stderr,
                "tcpsrv: unable to listen on port %d: %d\n",
                X128_PORT,
                WSAGetLastError());

        WSACleanup();

        return 1;
    }


    /*
     * Create caller listener.
     */
    callerListen =
        create_listener(CALLER_PORT);

    if (callerListen == INVALID_SOCKET)
    {
        fprintf(stderr,
                "tcpsrv: unable to listen on port %d: %d\n",
                CALLER_PORT,
                WSAGetLastError());

        closesocket(x128Listen);
        WSACleanup();

        return 1;
    }


    fprintf(stderr,
            "tcpsrv: X128 listening on TCP port %d\n",
            X128_PORT);

    fprintf(stderr,
            "tcpsrv: callers listening on TCP port %d\n",
            CALLER_PORT);


    /*
     * Persistent server loop.
     *
     * If X128 disconnects, TCPSRV stays alive and waits
     * for X128 to reconnect.
     */
    for (;;)
    {
        /*
         * Wait for VICE/X128.
         */
        x128Socket =
            accept(x128Listen,
                   NULL,
                   NULL);

        if (x128Socket == INVALID_SOCKET)
        {
            fprintf(stderr,
                    "tcpsrv: X128 accept failed: %d\n",
                    WSAGetLastError());

            break;
        }

        fprintf(stderr,
                "tcpsrv: X128 connected\n");

        callerSocket = INVALID_CLIENT;


        /*
         * Service X128 and caller connections.
         */
        for (;;)
        {
            FD_ZERO(&readSet);

            FD_SET(x128Socket,
                   &readSet);

            FD_SET(callerListen,
                   &readSet);

            if (callerSocket != INVALID_CLIENT)
            {
                FD_SET(callerSocket,
                       &readSet);
            }


            /*
             * Short timeout allows Windows console
             * keyboard polling.
             */
            timeout.tv_sec = 0;
            timeout.tv_usec = 50000;


            result =
                select(0,
                       &readSet,
                       NULL,
                       NULL,
                       &timeout);

            if (result == SOCKET_ERROR)
            {
                fprintf(stderr,
                        "tcpsrv: select failed: %d\n",
                        WSAGetLastError());

                break;
            }


            /*
             * Incoming BBS caller.
             */
            if (FD_ISSET(callerListen,
                         &readSet))
            {
                newSocket =
                    accept(callerListen,
                           NULL,
                           NULL);

                if (newSocket != INVALID_SOCKET)
                {
                    if (callerSocket != INVALID_CLIENT)
                    {
                        reject_busy_caller(newSocket);
                    }
                    else
                    {
                        callerSocket = newSocket;

                        fprintf(stderr,
                                "tcpsrv: caller connected\n");


                        /*
                         * Signal carrier and provide
                         * date/time to RBBS-128.
                         */
                        if (notify_connect(x128Socket)
                            == SOCKET_ERROR)
                        {
                            fprintf(stderr,
                                    "tcpsrv: lost X128 while notifying connection\n");

                            break;
                        }
                    }
                }
            }


            /*
             * X128 -> local console and caller.
             */
            if (FD_ISSET(x128Socket,
                         &readSet))
            {
                received =
                    recv(x128Socket,
                         (char *)buffer,
                         sizeof(buffer),
                         0);

                if (received == 0)
                {
                    fprintf(stderr,
                            "tcpsrv: X128 disconnected\n");

                    break;
                }

                if (received == SOCKET_ERROR)
                {
                    fprintf(stderr,
                            "tcpsrv: X128 recv failed: %d\n",
                            WSAGetLastError());

                    break;
                }


                /*
                 * Always show C128 output locally.
                 */
                fwrite(buffer,
                       1,
                       received,
                       stdout);

                fflush(stdout);


                /*
                 * Forward C128 output to the caller.
                 */
                if (callerSocket != INVALID_CLIENT)
                {
                    if (send_all(callerSocket,
                                 (const char *)buffer,
                                 received)
                        == SOCKET_ERROR)
                    {
                        fprintf(stderr,
                                "tcpsrv: caller send failed\n");

                        shutdown(callerSocket,
                                 SD_BOTH);

                        closesocket(callerSocket);

                        callerSocket =
                            INVALID_CLIENT;

                        notify_disconnect(x128Socket);

                        fprintf(stderr,
                                "tcpsrv: caller disconnected\n");
                    }
                }
            }


            /*
             * Caller -> X128.
             */
            if (callerSocket != INVALID_CLIENT &&
                FD_ISSET(callerSocket,
                         &readSet))
            {
                received =
                    recv(callerSocket,
                         (char *)buffer,
                         sizeof(buffer),
                         0);

                if (received == 0)
                {
                    /*
                     * Normal caller disconnect.
                     */
                    shutdown(callerSocket,
                             SD_BOTH);

                    closesocket(callerSocket);

                    callerSocket =
                        INVALID_CLIENT;

                    notify_disconnect(x128Socket);

                    fprintf(stderr,
                            "tcpsrv: caller disconnected\n");
                }
                else if (received == SOCKET_ERROR)
                {
                    /*
                     * Abnormal caller disconnect.
                     */
                    shutdown(callerSocket,
                             SD_BOTH);

                    closesocket(callerSocket);

                    callerSocket =
                        INVALID_CLIENT;

                    notify_disconnect(x128Socket);

                    fprintf(stderr,
                            "tcpsrv: caller connection lost\n");
                }
                else
                {
                    /*
                     * Ordinary caller data uses the
                     * faster 10 ms pacing.
                     */
                    if (send_to_x128(x128Socket,
                                     (const char *)buffer,
                                     received)
                        == SOCKET_ERROR)
                    {
                        fprintf(stderr,
                                "tcpsrv: X128 send failed\n");

                        break;
                    }
                }
            }


            /*
             * Windows console keyboard -> X128.
             *
             * Retained for development and future
             * SYSOP use.
             */
            while (_kbhit())
            {
                ch =
                    (unsigned char)_getch();


                /*
                 * Ignore Windows extended keys.
                 */
                if (ch == 0 || ch == 224)
                {
                    if (_kbhit())
                    {
                        (void)_getch();
                    }

                    continue;
                }


                /*
                 * Console input uses normal 10 ms
                 * C128 pacing.
                 */
                if (send_to_x128(x128Socket,
                                 (const char *)&ch,
                                 1)
                    == SOCKET_ERROR)
                {
                    fprintf(stderr,
                            "tcpsrv: X128 keyboard send failed\n");

                    break;
                }
            }
        }


        /*
         * Clean up caller if still connected.
         */
        if (callerSocket != INVALID_CLIENT)
        {
            shutdown(callerSocket,
                     SD_BOTH);

            closesocket(callerSocket);

            callerSocket =
                INVALID_CLIENT;
        }


        /*
         * Clean up X128 connection.
         */
        shutdown(x128Socket,
                 SD_BOTH);

        closesocket(x128Socket);


        /*
         * Stay alive and wait for X128 again.
         */
        fprintf(stderr,
                "tcpsrv: waiting for X128\n");
    }


    /*
     * Final shutdown.
     */
    closesocket(callerListen);
    closesocket(x128Listen);

    WSACleanup();

    return 0;
}
#5
One of the things I've always wanted to do is write a BBS for theC128 (several years ago we had a thread about this). So - I've started porting a CP/M version of RBBs (3.7 from 1981) over.

It's going to be heavily customised :

Although it will run on a real 128, the software is written to run under VICE (on Windows)

It has a custom tcp server (TCPSRV.EXE), utilises Swiftlink emulation, CMD hard disk emulation, JiffyDOS ROMS.

As I complete a task I'll update here.

#6
Microsoft BASIC began in 1975 as the company's very first product: a BASIC interpreter for the Intel 8080, written by Bill Gates and Paul Allen for the Altair 8800. That codebase was soon adapted to run on other 8-bit CPUs, including the MOS 6502, Motorola 6800, and 6809. You can learn more about this time and hear directly from Bill Gates on the Microsoft Learn Website's History of Microsoft video series or by visiting Bill Gates' blog.

The 6502 port was completed in 1976 by Bill Gates and Ric Weiland. In 1977, Commodore licensed it for a flat fee of $25,000, a deal that placed Microsoft BASIC at the heart of Commodore's PET computers and, later, the VIC-20 and Commodore 64. That decision put Microsoft's BASIC at the heart of Commodore's machines and helped millions of new programmers learn by typing:

10 PRINT "HELLO"
20 GOTO 10
This is BASIC M6502 8K VER 1.1, the 6502 BASIC lineage that powered an era of home computing and formed the foundation of Commodore BASIC in the PET, VIC-20, and the legendary Commodore 64. This very source tree also contains adaptations for the Apple II ("Applesoft BASIC"), built from the same core BASIC source. The original headers still read, "BASIC M6502 8K VER 1.1 BY MICRO-SOFT"—a time capsule from 1978.

The version we are releasing here—labeled "1.1"—contains fixes to the garbage collector identified by Commodore and jointly implemented in 1978 by Commodore engineer John Feagans and Bill Gates, when Feagans traveled to Microsoft's Bellevue offices. This is the version that shipped as the PET's "BASIC V2." It even contains a playful Bill Gates Easter egg, hidden in the labels STORDO and STORD0, which Gates himself confirmed in 2010.

https://opensource.microsoft.com/blog/2025/09/03/microsoft-open-source-historic-6502-basic
#7
PET hardware / PET Joystick Adapter
January 20, 2026, 06:25 AM
Sven Petersen has released a new piece of hardware for the PET.

A C64/VIC-20/Atari Joystick Adapter for the Commodore PET. It supports the Coyote Electronics pin configuration (two joysticks) and the space invader/break put style pin configuration (left, right, fire), found on zimmers.net (https://www.zimmers.net/anonftp/pub/cbm/pet/games/english/joy/index.html).

The Coyote Electronics pin configuration is also called "PET User port adapter" in the VICE emulator, so it is expected to be wide spread. Space Invader + is the completed pin configuration of the previously mentioned games and can also be found on the "stupid PET tricks" adapter. Hence the name. All joystick games found in the previously mention source could be played with this joystick adapter.

Visit https://github.com/svenpetersen1965/PET-Joystick-Adapter
#8
General chat / Commodore relaunch
November 01, 2025, 07:47 AM
I was aware that Commodore had re-launched, however I simply assumed it was another bunch of dreamers that had picked up and developed yet another hardware/software emulator. I didn't know that Leonard Tramiel (son of Commodore's founder Jack Tramiel) was on board as were some of Commodore's original team.

I shall watch with interest.
#9
News, views, help & info / Re: Updates - April 2025
September 22, 2025, 11:43 AM
Quote from: luvtofish on September 18, 2025, 04:08 AMI know this thread is a few months old. But are the download links also broken? I'm trying to download Blitz128 compiler and getting a 404?
Yes - I've got that on the list of things that need redoing.
#10

This is BASIC M6502 8K VER 1.1, the 6502 BASIC lineage that powered an era of home computing and formed the foundation of Commodore BASIC in the PET, VIC-20, and the legendary Commodore 64. This very source tree also contains adaptations for the Apple II ("Applesoft BASIC"), built from the same core BASIC source. The original headers still read, "BASIC M6502 8K VER 1.1 BY MICRO-SOFT"—a time capsule from 1978.

The version we are releasing here—labeled "1.1"—contains fixes to the garbage collector identified by Commodore and jointly implemented in 1978 by Commodore engineer John Feagans and Bill Gates, when Feagans traveled to Microsoft's Bellevue offices. This is the version that shipped as the PET's "BASIC V2."

The announcement from Microsoft is here and the code is here.

Supported Computer Systems

The source code includes conditional compilation support for multiple pioneering computer systems:

Apple II (REALIO=4)
Commodore PET (REALIO=3)
Ohio Scientific (OSI) (REALIO=2)
MOS Technology KIM-1 (REALIO=1)
PDP-10 Simulation (REALIO=0)

Technical Specifications

Language: 6502 Assembly Language
Target Processor: MOS Technology 6502 8-bit microprocessor
Memory Footprint: 8KB ROM version
Features: Complete BASIC interpreter with floating-point arithmetic
Architecture: Designed for both ROM and RAM configurations
Key Features
Programming Language Support
Full BASIC language implementation
Floating-point arithmetic
String handling and manipulation
Array support (both integer and string arrays)
Mathematical functions and operators
Input/output operations
Memory Management
Efficient memory utilization for 8-bit systems
String garbage collection
Dynamic variable storage
Stack-based expression evaluation
Hardware Abstraction
Configurable I/O routines for different computer systems
Terminal width adaptation
Character input/output abstraction
Optional disk storage support

Development History

The source code includes detailed revision history showing active development:

July 27, 1978: Fixed critical bugs in FOR loop variable handling and statement parsing
July 1, 1978: Memory optimization and garbage collection improvements
March 9, 1978: Enhanced string function capabilities
February 25, 1978: Input flag corrections and numeric precision improvements
February 11, 1978: Reserved word parsing enhancements
January 24, 1978: User-defined function improvements
#11
C / Other / Oscar64 C128 Menu Sample
August 22, 2025, 06:42 AM
/*
C128 Menu Example in C

File: menu.c
Author: Micha B 08/2025

Compiler: Oscar64
Compile me:
oscar64 -v -i=include -tm=c128 -O2 -dNOFLOAT -dHEAPCHECK menu.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <petscii.h>
#include <stdbool.h>
#include <ctype.h>

/* --- Global variables --- */
char c_choice = 0;

/* --- Function prototypes --- */
void draw_menu(void);
bool f_quit(void);
bool f_opt1(void);
bool f_opt2(void);
bool f_opt3(void);
bool f_opt4(void);

/* --- Draw a text menu --- */
void draw_menu(void)
{
putpch(147); // SCNCLR
printf("\x05================================\n"); // text color: White
printf("  C128 Menu Example\n");
printf("  -----------------\n");
printf("  (1) Option 1    (2) Option 2\n");
printf("  (3) Option 3    (4) Option 4\n");
printf("  (0) Quit\n");
printf("================================\n");
printf("\x1EChoice:\x05 "); // switch to text color green, than back to white
}

/* --- React on user's demand to quit --- */
bool f_quit(void)
{
char c_answer = 'n';

puts("Quit requested!\n\n");
puts("Do you realy want to quit (Y/n)? ");

c_answer = getpch();
if ( ((int) c_answer == 89)  || ((int) c_answer == 217)  )
return (false);
else
return(true);
}

/* --- React on option 1 --- */
bool f_opt1(void)
{
puts("Option 1 requested.\n\n");
puts(" Press a key to CONTINUE... ");
getpch(); // wait for key pressed
return(true);
}

/* --- React on option 2 --- */
bool f_opt2(void)
{
puts("Option 2 requested.\n\n");
puts(" Press a key to CONTINUE... ");
getpch(); // wait for key pressed
return(true);
}

/* --- React on option 3 --- */
bool f_opt3(void)
{
puts("Option 3 requested.\n\n");
puts(" Press a key to CONTINUE... ");
getpch(); // wait for key pressed
return(true);
}

/* --- React on option 4 --- */
bool f_opt4(void)
{
puts("Option 4 requested.\n\n");
puts(" Press a key to CONTINUE... ");
getpch(); // wait for key pressed
return(true);
}


/*  --- Main program --- */
int main(void)
{
bool running = true;

/* --- Prepare the environment --- */
// Switch display mode to lower case petscii
// could also be done by iocharmap(IOCHM_PETSCII_2);
putpch(14); // switch to charmap 2
putpch(8);     // lock Charmap selector key

while (running)
{
draw_menu();
c_choice = getpch();

/* --- Select options --- */
switch(c_choice)
{
case '0':
running = f_quit();
break;
case '1':
running = f_opt1();
break;
case '2':
running = f_opt2();
break;
case '3':
running = f_opt3();
break;
case '4':
running = f_opt4();
break;
default:
draw_menu();
}
}
puts("\n\nProgram terminated.\n");
putpch(9); // unlock charmap selector key
putpch(142); // switch back to charmap 1
return 0;
}
#12
We'll be upgrading the forum software next weekend - there will be a short outage.
The changes for this version are:
PHP Support
* Raises the minimum supported PHP version from 7.0 to 7.1.
* Adds support for PHP 8.4.
User Profiles
* Fixes errors when profile fields using the "nohtml" mask are given values with Unicode characters or quotation marks.
* Adds a new, separate permission for using Gravatars.
Attachments
* Several fixes and improvements for the attachment upload UI.
* Sets Content-Disposition header correctly for attachments for mobile browsers.
* Improves handling of SVG image attachments
* Adds support for WEBP images.
Editor and BBCode
* The "Quote Selected Text" button is now more tolerant of "messy" selection ranges.
* Shows "Last Edit" info (if applicable) when editing a post.
* Improves smileys popup behavior.
* Improves image quality of previews for embedded attachments in the WYSIWYG editor.
* Fixes bugs in a few edge cases when parsing BBCode.
* Centers BBCode tables wrapped in the center BBCode.
* Fixes PHP syntax highlighting in BBCode under PHP 8.3+.
* Fixes issues with broken HTML entities in warning templates.
Calendar
* Shows multiple calendar events on the same day correctly.
Search
* Fixes undefined variable error in custom search API.
* Fixes a bug where the last character of a Unicode search term could be mangled.
* Returns correct search results when post moderation is enabled.
Email
* Notification emails always use display names, never usernames.
* Improved handling of SMTP errors when sending emails.
* Fixes a bug where undeliverable emails might never clear from the mail queue.
* Accepts wildcards in email-based ban triggers.
Admin
* Updates Unicode data files in a background task. (This ensures the forum will always have up-to-date support for the latest emojis and such.)
* Fixes bug where rearranging boards in the admin center could cause some boards to temporarily not be shown.
Other
* Fixes errors in RSS feeds for messages by members with commas in their usernames.
* Fixes the integrate_error_types integration hook.
* Fixes the integrate_attachments_browse integration hook.
* Fixes open_basedir warnings in PHP 8.
* Fixes some minor UI bugs.
#13
PET programmers / Timing Table
May 25, 2025, 08:03 AM
Timing table for every PET command
#14
Commodore 128 C128 16-Color, 80-Column RGBi 15Khz RGB to VGA Adapter

PRICE:
17€ --> 8 colors
19€ --> 16 colors
36€ --> 16 colors + VLR
https://www.ebay.it/itm/376241376516

NEW C128 RGBi adapter to VGA!

Connect your C128 RGBi port to VGA monitor (15KHz horizontal freq. compatible!)

The anchoring screws for the C128 are present, as well as those for screwing for the VGA cable!

Just simply select the option that you want:

TYPE 1 --> 8-color 80-Column RGB

This adapter version is used to connect the rear 9-pin RGBI port on the Commodore 128 to a 15-pin VGA cable which then connects to a 15-kHz capable VGA monitor.
This gives you 8-color output and 80-Columns (double the 40-columns of the VIC-II chip) of text using the VDC Chip inside the 128.

TYPE 2 --> 16-color 80-Column RGB
This adapter version is used to connect the rear 9-pin RGBI port on the Commodore 128 to a 15-pin VGA cable which then connects to a 15-kHz capable VGA monitor.
This gives you 16-color output and 80-Columns (double the 40-columns of the VIC-II chip) of text using the VDC Chip inside the 128.

TYPE 3 --> 16-color 80-Column RGB + VLR (with USB power cable 1mt)
This adapter version is used to connect the rear 9-pin RGBI port on the Commodore 128 to a 15-pin VGA cable which then connects to a 15-kHz capable VGA monitor.This gives you 16-color output and 80-Columns with a PERFECT IMAGE because is equipped with VLR!
It is equipped internally with active VLR circuit. Power cable cord is included (USB connector 1mt) and you have to connect to standard AC/DC adapter (like smartphone)
This gives you 16-color output and 80-Columns with a PERFECT IMAGE because is equipped with VLR!
These vertical bars are visible especially in VGA mode on the first ASSY versions of the 128. The advice is that if you have already connected your c128 in 80 columns and you have these vertical bars on the VGA monitor then with this version you will notice an incredible improvement

PLEASE REMEMBER THAT:

This ADAPTER requires a VGA monitor that can sync to a 15-kHz signal. Normally, VGA uses a horizontal frequency of 31.5kHz. But, the Commodore 128 basically outputs a CGA signal which is a 15-kHz digital RGBI standard. It's this standard of the Commodore 128!
In any case there are some modern VGA monitors capable of displaying 15-kHz (Acer, Dell, Asus, BenQ, etc. etc).
#15
BASIC / ugBASIC 1.17.2
May 17, 2025, 07:57 AM
ugBASIC 1.17.2 Released

A new version of ugBASIC, the open source isomorphic BASIC compiler, has been released.
In addition to making some specific commands, such as DRAW and PLAY, compatible with multitasking, support for custom data types has been added at the language level via the BEGIN TYPE statement. The compiler has also been improved and optimized for Commodore 64, Commodore 64 REU and Commodore 128 computers.
ugBASIC is a cross compiler for Linux and Windows, for writing games in the BASIC language, running on dozens of different home computers and 8-bit consoles. There is also a convenient integrated IDE for Microsoft Windows and even a sandbox, if you don't want to install anything.


https://ugbasic.iwashere.eu/
#16
VIC 20 / VIC NX
May 09, 2025, 07:45 AM

From Thomas Lövskog
 
Today I got the new revision of my VIC NX* board soldered. It is now ready for hooking up to the logic analyser, oscilloscopes and start testing. Hopefully we can get this booting to the Ready prompt. The characters will be totally garbled though. The character ROM in this variant resides in SRAM and needs to be initialized. For that we need some code. Code that is executed in a hidden partition of the 512k for BLK6/7 before anything else. This is also where configuration firmware will reside.

Smoke test is done. All regulators work as expected, there is 5V where it should and Meatloaf is booting. The GreenPAK (little sub board in the top right corner. It will be soldered down as a tssop20 in the next run) manages the power, top LED and reset as expected.

However. To much work this weekend so it will have to be in this state until next week(end).

* VIC NX is my idea of an enhanced reproduction of the famous VIC 20. It is built on the 6502, 6522 and 6560/61 in DIP and the rest is modern of the shelf parts. Fully expanded and some extra features. See https://blog.lskg.io/tag/vic-nx/
The FATVIC, drop in replacement for 6560/61 is also shown here. This will be a nice test vehicle for that project.
#17
Gaming / Crawl 128
April 30, 2025, 01:11 PM
In development - a new game for the C128's VDC

https://goodwell.itch.io/crawl128

Hardware Requirements
This game is designed to run on the Commodore 128 (yes, in C128 mode), and is using the VDC-Chip (not the VIC-II chip, which is used in C64 mode). Also, the VDC-Chip needs to have 64kB of VRAM available.

Source Code
https://github.com/The8BitTheory/dungeoncrawlerjam2025
#18
We are pleased to announce the Commodore 128 4k Game Coding Competition 2025!

RULES

- Entries must be original programs
- Entries must NOT be larger than 4096 bytes (4kb)
- The result must be playable on a real (standard unexpanded) C128 and VICE
- The competition starts Saturday May 3rd 2025
- Entries must be submitted on or before June 30th 2025
- Entries must only be for the 128's 40 or 80 column modes - CP/M & 64 modes will be disqualified
- There will be a competition page at www.commodore-128.org with the submissions & you'll
  have the opportunity to vote for your favourite game

SUBMISSIONS

- Submissions page will be announced in the forum thread shortly
- Format of submissions to be in either D81, D64 or PRG format & then zipped

DISCUSSION

Competition discussion in the Commodore 128 Alive! forums : https://www.commodore-128.org/index.php/topic,3880.0.html

PRIZES

- 1st prize of $AU100
- 2nd prize of $AU50
- 3rd prize of $AU25

Prizes will be paid via Amazon voucher to the winners after voting has been completed & in the currency of the winner.
The amount paid will be determined by the exchange rate on the day the winners are announced.
#19
News, views, help & info / Updates - April 2025
April 12, 2025, 08:47 AM
When I moved the forums from the temp site to the hosted site I neglected to update a few settings - this resulted in some broken links - I've fixed most, will audit the board over the weekend to ensure that all links are correct again.

Also, my Stop Forum Spam API key wasn't added in to the board - this has now been corrected as well.
#20
VIC 20 / FATVIC
April 12, 2025, 08:13 AM
FATVIC is a drop in replacement for the 6560/6561 VIC chip used in the Commodore VIC 20 computer. It has all the usuall features of the 6560/6561, but also some extra optional features. Full memory in BLK0 accessable by the VIC chip, VGA mode, and enhanced functionallity with 32 sprites, hires graphics and fast hardware accelerated drawing and memory copying.

https://blog.deciduous.se/tag/fatvic/
#21
Herdware / Re: C128 - 40 years old
March 26, 2025, 06:29 AM
Mine was a demo model from the local dealer :)
#22
Herdware / Re: C128 - 40 years old
March 22, 2025, 06:08 AM
Quote from: BigDumbDinosaur on March 11, 2025, 11:12 PM
Quote from: Blacklord on January 06, 2025, 07:09 AM40 years ago today the Commodore 128 was released at the 1985 CES show in Las Vegas, Nevada.
Seems like just the other day when I bought my first C-128.
Yep - I can recall the day I bought my (metal) 128D and took it home.
#23
Switchless 8x Multi-Speeder and 32KB KB RAM expansion with parallel port and Kernal switch for the internal 1571 floppy disk drive and C128DCR (DolphinDos3 compatible)

https://github.com/FraEgg/commodore-c128dcr-1571-switchless-floppydrive-8x-multi-floppy-speeder
#24
PET hardware / ELTEC Graphics Card
March 04, 2025, 09:08 AM
A 256x256 Pixel graphic card for the Commodore PET computer from the small German computer engineering company ELTEC (1980)
#25
Programming / Multiply Bug
February 14, 2025, 01:54 PM
Most Commodore BASIC interpreters have a bug in the floating point multiply routine. The bug can be demonstrated with the attached BASIC program and also one that will correct the bug.